美文网首页
四、动态代理

四、动态代理

作者: 你的益达233 | 来源:发表于2019-11-26 22:35 被阅读0次

原理:

  1. 设计动态代理类(DymanicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由JVM来实现。(即在使用时再创建动态代理类和实例)
  2. 通过Java反射机制method.invoke(),通过调用动态代理类对象方法,从而自动调用目标对象的方法

优点:
只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复,多余代码
缺点:

  1. 效率低
    相比静态代理中直接调用目标对象(真实对象)方法,动态代理则需要通过Java反射机制从而间接调用目标对象方法
  2. 应用场景局限
    因为Java的单继承特性(每个代理类都继承了Proxy类),即只能针对接口创建代理类,不能针对类创建代理类

注:即只能动态代理实现了接口的类,该类可以extends Buy,但是后面必须implements某个接口,代理的实例也是接口的实例。

实例:对礼物进行包装
步骤1:声明处理器类(关键)

//声明处理器类
class DynamicProxy implements InvocationHandler {

    private Object proxyObject;

    //生成动态代理对象
    public Object newProxyInstance(Object subject){
        this.proxyObject = subject;
        return Proxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),this);
    }

    //动态代理对象调用目标对象任何方法前,都会调用处理器类的invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        //做额外操作
        System.out.println("包装礼物");
        result = method.invoke(proxyObject,args);
        return result;
    }
}

步骤2:声明目标对象类的抽象接口

interface SubjectBuy{

    void buyGift();
}

步骤3:声明目标对象类Buy1和Buy2

class Buy1 extends Buy implements SubjectBuy {

    @Override
    public void buyGift() {
        System.out.println("我要买mac");
    }


}

class Buy2 implements SubjectBuy {

    @Override
    public void buyGift() {
        System.out.println("我要买手表");
    }
}

class Buy{
    public void buy(){
        System.out.println("我要买买买");
    }
}

步骤4:通过动态代理对象,调用目标对象的方法

public class DynamicProxyDemo {


    public static void main(String[] args){
        DynamicProxy dynamicProxy = new DynamicProxy();

        Buy1 buy1 = new Buy1();
        SubjectBuy subjectBuy = (SubjectBuy) (dynamicProxy.newProxyInstance(buy1));
        subjectBuy.buyGift();

        Buy2 buy2 = new Buy2();
        SubjectBuy subjectBuy2 = (SubjectBuy)dynamicProxy.newProxyInstance(buy2);
        subjectBuy2.buyGift();
    }

}  

步骤4中注意点:首先接口是特殊的类,newProxyInstance返回的object只能向上转型为接口的对象。因为该object本身是有subject.getClass().getInterfaces()所具有的接口生成的。

源码分析

一、如何创建动态代理类及其实例对象

方法:Proxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),this)
解析:Proxy.newProxyInstance()作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类实例,并最终返回

//作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类及其对象实例,并最终返回
  
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException { 
  // 参数说明:
// 参数1:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
// 参数2:指定目标对象的实现接口
// 即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法
// 参数3:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象

...  
// 仅贴出核心代码

    // 1. 通过 为Proxy类指定类加载器对象 & 一组interface,从而创建动态代理类
    // >>关注3
    Class cl = getProxyClass(loader, interfaces); 
    
    // 2. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
    Constructor cons = cl.getConstructor(constructorParams); 
    
    // 3. 通过动态代理类的构造函数 创建 代理类实例(传入调用处理器对象
    return (Object) cons.newInstance(new Object[] { h }); 

// 特别注意 
// 1. 动态代理类继承 Proxy 类 & 并实现了在Proxy.newProxyInstance()中提供的一系列接口(接口数组)
// 2. Proxy 类中有一个映射表
  // 映射关系为:(<ClassLoader>,(<Interfaces>,<ProxyClass>) )
  // 即:1级key = 类加载器,根据1级key 得到 2级key = 接口数组
  // 因此:1类加载器对象 + 1接口数组 = 确定了一个代理类实例
...


}

总结:通过调用处理器类对象的Proxy.newProxyInstance()创建动态代理类及其实例对象(需传入目标类对象)
具体过程:
1.通过为Proxy类指定类加载器和一组接口,从而创建动态代理类的字节码,再根据字节码创建动态代理类
代码:Class cl = getProxyClass(loader, interfaces);

  1. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
    代码:Constructor cons = cl.getConstructor(constructorParams);
    3.通过动态代理类的构造函数创建代理类实例(传入调用处理器对象)
    代码:(Object) cons.newInstance(new Object[] { h })

如何通过调用动态代理对象方法,从而调用目标对象方法?

Proxy.newProxyInstance()生成了一个动态代理类 及其实例。
该动态代理类记为 :Proxy0Proxy0源码:

    <-- 动态代理类 $Proxy0 实现-->
// 继承:Java 动态代理机制的主类:java.lang.reflect.Proxy
// 实现:与目标对象一样的接口(即上文例子的Subject接口)
public final class $Proxy0 extends Proxy implements Subject {

    // 构造函数
        public ProxySubject(InvocationHandler invocationhandler)   
        {   
        super(invocationhandler);   
    }  
    
     // buybuybuy()是目标对象实现接口(Subject)中的方法
     // 即$Proxy0类必须实现
     // 所以在使用动态代理类对象时,才可以调用目标对象的同名方法(即上文的buybuybuy())
     public final void buybuybuy() {
        try {
            super.h.invoke(this, m3, null); 
            // 该方法的逻辑实际上是调用了父类Proxy类的h参数的invoke()
            // h参数即在Proxy.newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)传入的第3个参数InvocationHandler对象
            // 即调用了调用处理器的InvocationHandler.invoke()
            // 而复写的invoke()利用反射机制:Object result=method.invoke(proxied,args)
            // 从而调用目标对象的的方法 ->>关注4
        return;
        } catch (Error e) {

        } catch (Throwable throwable) {
             throw new UndeclaredThrowableException(throwable);
    }
}

这里源码沾的有点乱,具体看别人的https://www.jianshu.com/p/5dc416ea58a2

此类源码能说明很多问题

  1. 动态代理类已经继承了Proxy父类,所以动态代理类没法再代理实体父类,只能代理接口,只能代理实现接口的类
  2. 动态代理类会实现目标类的所实现的接口,这样就具有目标类的相同实现的方法,然后在实现方法中调用的是super.h.invoke(this, m3, null)。刚好是处理器类重写的invoke方法,而复写的invoke()利用反射机制:Object result=method.invoke(proxied,args)--->从而调用了目标对象的方法

总结:

  1. 动态代理类实现了与目标类一样的接口,并实现了需要目标类对象需要调用的方法
  2. 该方法的实现逻辑 = 调用父类 Proxy类的 h.invoke()
  3. 在 InvocationHandler.invoke() 中通过反射机制,从而调用目标类对象的方法

参考:https://www.jianshu.com/p/5dc416ea58a2

相关文章

  • java动态代理

    本文从四个方面认识动态代理 什么是代理? 为什么使用代理? 如何使用动态代理? 动态代理的原理 一 什么是代理 ...

  • 面试系列~动态代理实现与原理

    动态代理有JDK动态代理, CGLIB动态代理, SpringAOP动态代理 一,JDK动态代理  jdk动态代理...

  • 编程常用的设计模式

    动态代理和静态代理 静态代理 动态代理 静态代理与动态代理的区别 JDK中的动态代理和CGLIB 实现动态代理的方...

  • 四、动态代理

    原理: 设计动态代理类(DymanicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接...

  • Spring的AOP原理分析

    一 动态代理 动态代理分为JDK动态代理和CGLIB动态代理 jdk动态代理 被代理类(目标类)和代理类必须实现同...

  • 设计模式之代理模式

    代理分为静态代理和动态代理。 动态代理又包括基于JDK的动态代理、基于CGlib 的动态代理、基于Aspectj实...

  • Java高级主题(五)——动态代理

    代理可以分为静态代理、动态代理,动态代理又可以分为 jvm的动态代理 和 cglib的动态代理。像spring框架...

  • 动态代理

    动态代理分为两类:1、基于接口的动态代理; (JDK动态代理 )2、基于类的动态代理;(cglib动态代理)3、J...

  • 动态代理的两种方式

    静态代理就不说了,基本用到的都是动态代理。 Java中动态代理有JDK动态代理和CGLIB动态代理。 JDK代理的...

  • JDK动态代理使Spring事务失效

    1.JDK 动态代理 简单的动态代理: 接口 接口实现类 代理类 测试 2.Spring事务 事务有四个特性:AC...

网友评论

      本文标题:四、动态代理

      本文链接:https://www.haomeiwen.com/subject/aoquwctx.html