接住喽????,送你个装逼的技能: JDK动态代理

       

今天讲一个比较深层的知识点:JDK动态代理,这是个可以让小白在大咖面前装逼的神器,顺便送你一个代理模式的温习机会。

代理模式场景

为了引出动态代理的用法,我们先看看代理设计模式,这能让你了解JDK动态代理的应用场景的同时,让你记忆深刻。代理模式是通过代理对象作为中间人来访问目标对象,这样可以完美的遵循开闭原则,从而避免修改目标对象来满足需求而降低可维护性。

现实生活中比较常见的各种中间商、经纪人都是活生生的代理模式例子。咱们拿明星经纪人来说

上述关系现实中还是非常复杂的,比如什么干爹啦、劈腿啦、潜规则啦,巴拉巴拉的.....剥茧抽丝后,其实流程可简化这样

老板不直接接触明星,而是直接和经纪人商谈,毕竟经纪人无论在经验和精力上都有优势。这里经纪人其实就是代理对象,明星就是目标对象,老板就是调用方。转换为代码流程如下

这里需要认真的强调一点:代理模式侧重于控制访问,代理对象不会改变目标对象的职责和能力,它提供与目标对象相同的接口,但会增加相应的逻辑来控制访问目标对象。

有些网友提出代理模式是为了在目标对象的基础上增强功能,如果较真的话小编并不不认同此种说法,因为增强基类的功能那是装饰模式干的活;代理模式和委托模式也有区别,后者是为了提供统一的接口服务,便于方便切换底层实现。

如果大家对设计模式感兴趣,可以留言给 码农神说 提出“设计模式系列专题”的文章需求。

代理模式实现

代理模式的演示实现如下(为了方便观众观看,我会把代码集中在App.java中,项目中不允许)

public class App{
  public static void main( String[] args )
  {
    //代理对象(经纪人)
    Broker broker = new Broker();

    System.out.println("A老板的唱歌演出需求");
    broker.doSing();
    System.out.println("---------------");
    System.out.println("B老板的跳舞演出需求");
    broker.doDance();
  }
}

/**
 * 经纪人
 */
class Broker{
  /**
   * 唱歌演出
   */
  public void doSing(){
    //演出前业务处理
    System.out.println("1.检查节目是否和明星的调性匹配");
    System.out.println("2.出场费是否满足");
    //演出
    new Star().doSing();
    //演出后业务处理
    System.out.println("1.出场费尾款");
    System.out.println("2.粉丝维护");
  }
  /**
   * 跳舞演出
   */
  public void doDance(){
    //演出前业务处理
    System.out.println("1.检查节目是否和明星的调性匹配");
    System.out.println("2.出场费是否满足");
    //演出
    new Star().doDance();
    //演出后业务处理
    System.out.println("1.出场费尾款");
    System.out.println("2.粉丝维护");
  }
}

/**
 * 明星
 */
class Star{
  /**
   * 唱歌技能
   */
  public void doSing(){
    System.out.println("唱歌");
  }
  /**
   * 跳舞技能
   */
  public void doDance(){
    System.out.println("跳舞");
  }
}

老板不直接接触明星,而是通过经纪人满足业务需求。细心的同学会发现,经纪人的业务处理中存在大量重复代码,当然你可以把演出前后的业务封装成方法调用如

/**
 * 唱歌演出
 */
public void doSing(){
    //演出前业务处理
    beforeDo();
    //演出
    new Star().doSing();
    //演出后业务处理
    afterDo();
}

但依然不美,试想如果能把经纪人的业务技能直接一一匹配到明星的技能(doSing,doDance)就方便了,于是引出了JDK动态代理。

用JDK动态代理重构

1. 为了实现“动态”需要使用面向接口的编程思想,把明星和经纪人抽象出一个共同的接口

/**
 * 明星和经纪人的接口
 */
interface IAct {
    void doSing();
    void doDance();
}

2. 通过实现InvocationHandler接口来做代理业务

/**
 * 经纪人
 */
class Broker implements InvocationHandler {
  //目标对象
  private Object target;
  public Broker(Object target){
    this.target=target;
  }

  @Override
  public Object invoke(Object proxy, Method method, 
        Object[] args) throws Throwable {
    //演出前业务处理
    System.out.println("1.检查节目是否和明星的调性匹配");
    System.out.println("2.出场费是否满足");

    //明星演出技能
    Object object=method.invoke(target,args);

    //演出后业务处理
    System.out.println("1.出场费尾款");
    System.out.println("2.粉丝维护");

    return object;
  }
}

3. 动态创建代理对象并处理业务

public static void main( String[] args ){
  //目标对象
  IAct star=new Star();

  //使用newProxyInstance创建动态代理对象
  IAct broker=(IAct) Proxy.newProxyInstance(
          IAct.class.getClassLoader(),
          star.getClass().getInterfaces(),
          new Broker(star)
  );

  //业务处理
  System.out.println("A老板的唱歌演出需求");
  broker.doSing();
  System.out.println("---------------");
  System.out.println("B老板的跳舞演出需求");
  broker.doDance();
}

经纪人执行doSing,经过业务逻辑处理后直接映射到明星的doSing,这样就减少了很多重复的代码,提高了可维护性。

总结JDK实现代理模式流程如下

1. 抽象出目标对象的接口

2. 实现接口InvocationHandler创建代理业务

3. 使用newProxyInstance创建代理对象

4. 业务处理

全部代码如下

package com.zhaiqianfeng;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class App{
  public static void main( String[] args ){
    //目标对象
    IAct star=new Star();

    //使用newProxyInstance创建动态代理
    IAct broker=(IAct) Proxy.newProxyInstance(
            IAct.class.getClassLoader(),
            star.getClass().getInterfaces(),
            new Broker(star)
    );

    //业务处理
    System.out.println("A老板的唱歌演出需求");
    broker.doSing();
    System.out.println("---------------");
    System.out.println("B老板的跳舞演出需求");
    broker.doDance();
  }
}

/**
 * 明星和经纪人的接口
 */
interface IAct {
  void doSing();
  void doDance();
}

/**
 * 经纪人
 */
class Broker implements InvocationHandler {
  //目标对象
  private Object target;
  public Broker(Object target){
    this.target=target;
  }

  @Override
  public Object invoke(Object proxy, Method method, 
          Object[] args) throws Throwable {
    //演出前业务处理
    System.out.println("1.检查节目是否和明星的调性匹配");
    System.out.println("2.出场费是否满足");

    //明星演出技能
    Object object=method.invoke(target,args);

    //演出后业务处理
    System.out.println("1.出场费尾款");
    System.out.println("2.粉丝维护");

    return object;
  }
}

/**
 * 明星
 */
class Star implements IAct {
  /**
   * 唱歌技能
   */
  public void doSing(){
    System.out.println("唱歌");
  }
  /**
   * 跳舞技能
   */
  public void doDance(){
    System.out.println("跳舞");
  }
}

Java8 lambda表达式重构

如果只是临时业务处理,可以使用匿名类或Java8的lambda表达式可以更优

public static void main(String[] args) {
  //目标对象
  IAct star = new Star();

  //使用newProxyInstance创建动态代理
  IAct broker = (IAct) Proxy.newProxyInstance(
      IAct.class.getClassLoader(),
      star.getClass().getInterfaces(),
      (proxy, method, args2) -> {
        //演出前业务处理
        System.out.println("1.检查节目是否和明星的调性匹配");
        System.out.println("2.出场费是否满足");

        //明星演出技能
        Object object = method.invoke(star, args2);

        //演出后业务处理
        System.out.println("1.出场费尾款");
        System.out.println("2.粉丝维护");

        return object;
    }
  );

  //业务处理
  System.out.println("A老板的唱歌演出需求");
  broker.doSing();
  System.out.println("---------------");
  System.out.println("B老板的跳舞演出需求");
  broker.doDance();
}

写在最后

JDK动态代理实际上是在运行时通过反射的方式来实现的,将代理的方法调用转到到目标对象上,最终将目标对象生成的任何结果返回给调用方。由于这是个链式调用,所以很方便代理在目标对象方法调用前后增加处理逻辑。根据这种思路可以在多种设计模式中使用JDK的动态代理比如代理模式、Facade、Decorator等。

在面向方面编程(AOP)也应用广泛,如事务管理、日志记录、数据校验等,主要是将横切关注点从业务逻辑中分离出来,所以一通百通。

补充一点,由于JDK的不断优化,到JDK8的时候JDK的动态代理不比CGLIB效率低,大家可以做些实验。

End

版权归@码农神说所有,转载须经授权,翻版必究

转载可联系助手,微信号:codeceo-01

往期精彩

 

给“小白”漫画+图示讲解MyBatis原理,就问香不香!

 

辟谣:程序员不配谈恋爱?你错的可以!真相来了

 

面试官:CAP都搞不清楚,别跟我说你懂微服务!

 

互联网人的娱乐精神之28岁退休 & P8和生活助理的故事

 

千万不能让程序员给娃娃取名字

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值