动态代理
众所周知,动态代理可以帮助我们修饰真实类的行为,并且可以对行为结果、行为整体等进行改变。比如Spring会对符合事务条件的方法前后进行事务操作,其原理就是通过动态代理完成。
在方法执行前,通过事务管理器创建连接,关闭自动提交,再根据方法有无异常进行提交或回滚操作,当然真实的操作肯定没有这么简单,因为还涉及到事务传播等等。本文就带大家了解下JDK动态代理的原理实现。
先来体验下JDK动态代理:
- 首先创建一个接口
public interface ProxyInterface {
String execute(String msg);
}
- 创建一个真实类,实现该接口
public class RealObject implements ProxyInterface {
@Override
public String execute(String msg) {
System.out.println("真实方法");
return msg;
}
}
- 创建一个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;
}
}
- 创建一个获取代理对象的类,通过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));
}
}
- 在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);
}
}
- 结果如下
为什么会出现这样的结果呢?
原因
- 首先我们看Proxy.newProxyInstance()方法需要传递的参数
ClassLoader loader --> 加载真实类的类加载器
Class<?>[] interfaces --> 真实类所实现的接口列表
InvocationHandler h --> 代理类需要实现的行为描述
- 第一个参数:类加载器,代理类需要通过类加载器去加载到内存。
- 第二个参数:接口数组,代理类需要知道它实现的接口是什么。
- 第三个参数:调用处理器,代理类最终方法实现就在InvocationHandler.invoke()方法中。
- 直接查看Proxy.newProxyInstance()方法源码
简单来讲,只有这几步比较关键,当然为什么要这样做,我们看看编译后的代理类具体长什么样子。
看,首先它默认继承了Proxy类,所以这也是为什么JDK动态代理为什么必须有接口的原因,因为Java是 【单继承】。
并且代理类将接口所有的方法都以静态变量的形式存储,在静态代码块中进行赋值。
并且,他将我们自定义的InvocationHandler存在父类中,在Proxy类中有一个构造器。
再来看看代理类所实现的execute()方法是怎么操作的
- 首先,它实现了execute()方法,拥有了和真实类一样的行为。
- 执行代理类的execute()方法时,它会调用Proxy类中的我们自己实现的InvocationHandler类的invoke()方法。
- 在invoke()方法传入的参数是 代理类本身、execute()方法本身的对象、方法参数
我们再结合InvocationHandler实现类来看
Debug模式下查看:
一开始我们维护了一个真实对象,在invoke()方法中,执行前置操作后,接着使用真实对象执行了execute()方法,再执行后置操作。
那么在代理类执行execute()方法时,其实执行的是我们实现的InvocationHandler.invoke()方法,而在这个方法中我们又执行了真实对象的方法。
简单来讲,这就是JDK动态代理的原理。
总结
- Proxy.newProxyInstance()方法加载代理类,并继承Proxy类,还实现了我们传入的接口(因为它需要与真实类有共同的行为)。
- 代理类中维护着InvocationHandler的实现类,在调用代理类的方法时,实际上调用的是InvocationHandler.invoke()方法,而invoke()方法是人为定义的,因此就实现了JDK动态代理,由人为规定其行为、结果等。
- 并且因为代理类已经继承了Proxy类,而Java又是单继承,所以就只能在接口上做文章,代理类必须实现接口,不然无法与真实类有共同行为,自然也就起不到代理的效果。