Bootstrap

【动态代理详解】


动态代理

  众所周知,动态代理可以帮助我们修饰真实类的行为,并且可以对行为结果、行为整体等进行改变。比如Spring会对符合事务条件的方法前后进行事务操作,其原理就是通过动态代理完成。

  在方法执行前,通过事务管理器创建连接,关闭自动提交,再根据方法有无异常进行提交或回滚操作,当然真实的操作肯定没有这么简单,因为还涉及到事务传播等等。本文就带大家了解下JDK动态代理的原理实现。

先来体验下JDK动态代理:

  1. 首先创建一个接口
public interface ProxyInterface {
    String execute(String msg);
}
  1. 创建一个真实类,实现该接口
public class RealObject implements ProxyInterface {
    @Override
    public String execute(String msg) {
        System.out.println("真实方法");
        return msg;
    }
}
  1. 创建一个InvocationHandler的实现类,目的是约定代理对象要做的行为
public class ProxyInvocationHandler implements InvocationHandler {

    /**
     * 维护一个真实对象的实例,反射调用方法需要该对象的实例
     */
    private final Object target;

    public ProxyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行代理方法前");
        // 反射调用方法:target为该对象的实例,表示调用该对象的此方法;args为方法参数
        Object invoke = method.invoke(target, args);
        System.out.println("执行代理方法后");
        return invoke;
    }
}
  1. 创建一个获取代理对象的类,通过Proxy.newProxyInstance() 方法获取代理对象
public class ProxyFactory {
    /**
     * 获取代理对象
     *
     * @param target 真实对象 (请传接口引用,这样因为泛型的原因,可以不用类型转换)
     * @param <T>    泛型(接口)
     * @return 代理对象
     */
    @SuppressWarnings("all")
    public static <T> T getProxy(T target) {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                          target.getClass().getInterfaces(),
                                          new ProxyInvocationHandler(target));
    }
}
  1. 在Test类中创建真实对象、获取代理对象、通过代理对象执行execute()方法
public class ProxyTest {
    public static void main(String[] args) {
        ProxyInterface realObject = new RealObject();
        ProxyInterface proxy = ProxyFactory.getProxy(realObject);
        String execute = proxy.execute("失败");
        System.out.println(execute);
    }
}
  1. 结果如下

在这里插入图片描述


为什么会出现这样的结果呢?

原因

  1. 首先我们看Proxy.newProxyInstance()方法需要传递的参数
ClassLoader loader      -->  加载真实类的类加载器
Class<?>[] interfaces   -->  真实类所实现的接口列表
InvocationHandler h     -->  代理类需要实现的行为描述 
  • 第一个参数:类加载器,代理类需要通过类加载器去加载到内存。
  • 第二个参数:接口数组,代理类需要知道它实现的接口是什么。
  • 第三个参数:调用处理器,代理类最终方法实现就在InvocationHandler.invoke()方法中。

  1. 直接查看Proxy.newProxyInstance()方法源码

在这里插入图片描述


   简单来讲,只有这几步比较关键,当然为什么要这样做,我们看看编译后的代理类具体长什么样子。

在这里插入图片描述


   看,首先它默认继承了Proxy类,所以这也是为什么JDK动态代理为什么必须有接口的原因,因为Java是 【单继承】。

   并且代理类将接口所有的方法都以静态变量的形式存储,在静态代码块中进行赋值。

   并且,他将我们自定义的InvocationHandler存在父类中,在Proxy类中有一个构造器。

在这里插入图片描述


   再来看看代理类所实现的execute()方法是怎么操作的

在这里插入图片描述

  1. 首先,它实现了execute()方法,拥有了和真实类一样的行为。
  2. 执行代理类的execute()方法时,它会调用Proxy类中的我们自己实现的InvocationHandler类的invoke()方法。
  3. 在invoke()方法传入的参数是 代理类本身、execute()方法本身的对象、方法参数

   我们再结合InvocationHandler实现类来看

在这里插入图片描述

Debug模式下查看:

在这里插入图片描述


   一开始我们维护了一个真实对象,在invoke()方法中,执行前置操作后,接着使用真实对象执行了execute()方法,再执行后置操作。

   那么在代理类执行execute()方法时,其实执行的是我们实现的InvocationHandler.invoke()方法,而在这个方法中我们又执行了真实对象的方法。

   简单来讲,这就是JDK动态代理的原理。


总结

  1. Proxy.newProxyInstance()方法加载代理类,并继承Proxy类,还实现了我们传入的接口(因为它需要与真实类有共同的行为)。
  2. 代理类中维护着InvocationHandler的实现类,在调用代理类的方法时,实际上调用的是InvocationHandler.invoke()方法,而invoke()方法是人为定义的,因此就实现了JDK动态代理,由人为规定其行为、结果等。
  3. 并且因为代理类已经继承了Proxy类,而Java又是单继承,所以就只能在接口上做文章,代理类必须实现接口,不然无法与真实类有共同行为,自然也就起不到代理的效果。







;