最全介绍的代理模式---第二节-JDK 动态代理

Posted AnWen~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最全介绍的代理模式---第二节-JDK 动态代理相关的知识,希望对你有一定的参考价值。

五、动态代理

优势:在静态代理中目标类很多时候,可以使用动态代理,避免静态代理的缺点:

动态代理中目标类即使有很多:

1)代理类数量可以很少

2)当你修改了接口中的方法时,不会影响代理类

1、介绍

​ 在程序执行过程中,由JVM根据反射机制,创建代理类对象,并动态的指定要代理的目标类。换句话说,动态代理是一种创建Java对象的能力,让你不需要定义代理类的 .java源文件(如:Taobao类),就能创建代理类对象。

​ 其实,就是jdk运行期间,动态创建class字节码并加载到JVM中

动态:在程序执行时,调用 JDK 提供的方法才能创建代理类的对象

【注】

1)动态代理:使用反射机制,在程序执行中,创建代理类对象;代理的目标类是活动的,可设置的。如,设置不同的目标类,给不同的目标随时创建代理。

2)静态代理:代理类时手工实现的Java文件;代理的目标对象是规定的。

在Java中,要想创建对象:

1)创建类文件,JVM把文件编译为class

2)使用构造方法,创建类的对象

2、实现方法

(1)JDK动态代理:使用Java反射包中的类和接口实现动态代理的功能。

反射包:Java.lang.reflect,主要用到:InvocationHandler、Method、Proxy

(2)Cglib动态代理:Cglib是第三方的工具库,创建代理对象。

原理:继承。Cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。

​ 因为Cglib是继承、重写方法,所以要求目标类不能是final的,方法也不能是final的。Cglib的要求目标类比较宽松,只要能继承就可以了。Cglib在很多框架中使用,比如,Mybatis、spring框架中都有使用。

​ Cglib(Code Generation Library)是一个开源项目,是一个强大的、高性能的、高质量的Code生产类库,它可以在运行期扩展Java类与实现Java接口,广泛被许多的AOP的框架中使用,如,Spring AOP。

​ 使用 JDK 的 Proxy 实现代理,要求目标类与代理类实现相同的接口,若目标类不存在接口,则无法使用该方法实现。

​ 对于无接口的类,要为其创建动态代理,就要使用Cglib来实现。Cglib 代理的生成原理是生成目标类的子类,而子类是增强的,这个子类对象就是代理对象。所以,使用 Cglib 生成动态代理,要求目标类必须能够被继承,即不能是final类

3、JDK动态代理

反射包:Java.lang.reflect,主要用到:InvocationHandler、Method、Proxy

(1)InvocationHandler 接口----调用处理器【实际上表示你这代理到底要干什么!!】

事件处理,执行目标对象的方法时,会触发事件处理器方法,会把当前执行的目标对象方法作为参数传入。

接口中就一个方法 invoke(),表示代理对象要执行的功能代码,换句话说,代理类要实现的功能(下面列举的两种功能)放在invoke() 中

  • 代理类完成的功能:

调用目标方法,执行目标方法的功能;

功能增强,在目标方法调用时,增强功能。

  • 代码:
public interface InvocationHandler 
    // proxy JDK创建的代理对象,无需赋值,不需要你管理,没什么用
    // method 目标对象中的方法,JDK提供Method对象,就是要调用目标对象的某个方法的Method对象
    // args 调用目标对象某个方法时接受的参数,JDK提供的,不需要认为参与
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;


  • 怎么用

创建类,实现InvocationHandler接口

重写invoke(),吧原来静态代理中的代理类要完成的功能写在这里

(2)Method 类------表示方法的,确切地说是目标类中的方法

作用:通过Method可以执行某个方法类的方法:Method.invoke()

【注】这里的invoke与上面提到的invoke 完全不一样,只是名字碰巧一样。

  • 上面提及的invoke:接口中的方法;
  • 这里提及的invoke:类中的方法。

在动态代理中的使用方法:method.invoke(目标对象,方法的参数args);

【注】可以执行任一对象的方法,而不需要知道这个方法的名称

说明:method.invoke() 就是用来执行目标方法的,等同于静态代理中的

float price = factory.sell(amount);

(3)Proxy 类:核心的对象,创建代理对象。之前创建对象都是new类的构造方法()。现在我们是使用proxy类的方法,代替new的使用。

  • 方法:静态方法 newProxyInstance()

  • 作用是:创建代理对象,等同于静态代理中的 TaoBao taoBao=new TaoBao()

  • 代码:

public class Proxy implements java.io.Serializable 
	@CallerSensitive
    // 这里会用到反射机制
    // ClassLoader loader 当前目标对象使用的类加载器,获取加载器的方固定:target.getClass().getClassLoader()
    // Class<?>[] interfaces 目标对象要实现的接口,用泛型确认类型:target.getClass().getInterfaces()。实际上,这个参数给代理对象提供了一组接口,这个代理对象会实现这些接口。
    //  InvocationHandler h 我们自己写的,代理类要完成的功能
    // 返回对象 ---> 代理对象
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    ....此处省略

(4)实现动态代理的步骤:

创建接口,定义目标类要完成的功能;

创建目标类实现接口;

创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能:

  • 调用目标方法
  • 增强功能

使用Proxy类的静态方法newProxyInstance(),创建代理对象,并把返回值转为接口类型。

(5)UML类图:

(6)代码:

***UsbSell接口

// 表示功能的厂家,或者商家要完成的功能
public interface UsbSell 
    // 定义方法
    // amount: 表示一次购买的数量,暂时不用
    // 返回表示一个U盘的价格
    float sell(int amount);

    // 可以定义多个其他的方法

***UsbKingFactory类

// 目标类:金士顿厂家,不接用户的单独购买
public class UsbKingFactory implements UsbSell 
    @Override
    public float sell(int amount) 
        // 一个128G的U盘85元
        // 后期可以根据amount,可以实现不同的价格,比如1000个,单价85元,
        System.out.println("目标类中,执行sell目标方法");
        return 85.0f;
    

***MySellHandler类

// 必须实现InvocationHandler,完成代理类需要完成的功能(1、待用目标方法;2、功能增强)
public class MySellHandler implements InvocationHandler 
    private Object target = null;
    // 动态代理:目标对象是活动的,不是固定的,需要传入进来
    // 传入是谁,就给谁创建代理对象
    public MySellHandler(Object target)
        this.target = target;
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        // 向厂家发送订单,告诉厂家,我买了U盘,厂家发货
        //float price = factory.sell(amount);//厂家的价格
        Object res = method.invoke(target,args);
        // 商家 需要加价,也就是dialing要增加价格
        // 这里相对于目标对象,就增强了功能
        // 代理类 在完成目标类方法调用后,增强了功能
        //price += 25;
        if(res!=null)
            Float price = (Float) res;//强转
            price += 25;
            res = price;
        
        // 在目标类的方法调用后。你做的其他功能,都是增强功能,如发放优惠券
        System.out.println("淘宝商家,给你返回一个优惠券,或者红包");
        return res;
    

***MainShop(客户端)

public class MainShop 
    public static void main(String[] args) 
        //1.创建对象,使用Proxy
        //2.创建目标对象
        UsbSell factory = new UsbKingFactory();
        //3.创建Invocationhandler对象
        InvocationHandler myHandle = new MySellHandler(factory);

        //4.创建代理对象
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance
                (factory.getClass().getClassLoader(), // 通过反射得到目标对象的类加载器
                factory.getClass().getInterfaces(), // 通过反射得到目标对象的接口
                myHandle); // 执行功能的Handle
        // 这里 proxy.getclass().getName() = com.sun.proxy.$Proxy0 ,是可以转为目标对象所实现的所有接口
        System.out.println(proxy.getClass().getName());
        // factory.getClass().getInterfaces() 这个参数就说明了 目标对象 UsbKingFactory 有哪些,在创建代理对象的时候,会让代理对象自动实现这些接口,同时这个代理对象本身也实现Proxy
        // 只是这里进行了强转,转为 目标对象 UsbKingFactory 实现的接口 UsbSell
        //通过代理执行方法
        float price = proxy.sell(1);
        // 在执行这个 代理对象 执行 目标方法的时候,实际上用的是 myHandle 中的 invoke()。
        // sell 方法 赋给 myHandle 中的 invoke 的 参数 method,参数 是 1
        System.out.println("通过动态代理对象,调用方法:" +price);
    

(7)JDK动态代理执行流程

最后,要知道动态代理是做什么的?

​ 可以在不改变原来目标方法功能的前提下,可以在代理中增强自己的功能代码

比如,在项目开发中,有一个功能是其他人(其他部门,其他小组的人)写好的,就可以使用动态代理,在 别人的基础上在自己的代理中增强功能。

以上是关于最全介绍的代理模式---第二节-JDK 动态代理的主要内容,如果未能解决你的问题,请参考以下文章

最全介绍的代理模式---第二节-JDK 动态代理

JDK动态代理[1]----代理模式实现方式的概要介绍

模式的秘密-代理模式-JDK动态代理

JDK动态代理[2]----JDK动态代理的底层实现之Proxy源码分析

设计模式----代理模式

第二节 Mapper代理开发模式