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

原创
07/04 13:00
阅读数 24

       

今天讲一个比较深层的知识点: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和生活助理的故事
千万不能让程序员给娃娃取名字



本文分享自微信公众号 - 码农神说(codeceo)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部