一、代理模式
代理模式(Proxy):给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
代理类Proxy与被代理类RealSubject均实现了Subject接口,并且Proxy持有RealSubject对象的引用。当Client调用Request()方法时,将调用Proxy.Request(),并由Proxy.Request()代理调用realSubject.Request()
二、JDK动态代理
JDK中基于反射实现了动态代理,可以在程序运行时生成代理类。其核心在于java.lang.reflect.InvocationHandler与 java.lang.reflect.Proxy,InvocationHandler是一个接口只包含invoke方法。
1 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; |
首先需要实现该接口,并且持有被代理类target的引用,当代理类的任何方法被调用时会转发到调用invoke方法。因此,可以在invoke方法中添加代理逻辑比如:打印日志、安全审计等,最后再通过反射调用被代理类的方法。
1 | public class InvocationHandlerImpl<T> implements InvocationHandler { |
而在程序运行时动态生成代理类则是依靠Proxy.newProxyInstance方法,该方法的参数分别为:
- ClassLoader loader,类加载器
- Class<?>[] interfaces,代理类需要实现的接口列表
- InvocationHandler h,上文实现的代理执行类
1 | public static Object getProxy(Object target) { |
生成的代理类已经实现了指定的接口,因此使用起来与正常的实现没有任何区别1
2
3
4public static void main(String[] args) {
HelloService proxyHelloService = (HelloService) getProxy(new HelloServiceImpl());
proxyHelloService.hello("drowyue");
}
输出如下:
1 | before hello |
三、原理
Proxy.newProxyInstance的核心代码如下,首先,getProxyClass0方法生成代理类,然后通过反射创建该代理类的对象并返回
1 | public static Object newProxyInstance(ClassLoader loader, |
进一步跟踪getProxyClass0方法,并将实际生成代理类保存并反编译如下。代理类继承Proxy并实现了HelloService,m0~m3四个变量分别为equals、toString、hashCode、hello,四个方法。每一个方法的实现都是调用super.h.invoke方法,而父类中的h变量正是前面定义的InvocationHandler,从而实现了将接口中每个方法均代理到InvocationHandler.invoke中。
1 | public final class HelloServiceImpl extends Proxy implements HelloService { |
四、总结
JDK动态代理特点
- JDK动态代理只能对接口进行代理
- 生成的代理类都会关联一个InvocationHandler对象,在代理类上调用接口中的方法均会最终调用InvocationHandler.invoke方法执行
- 除了接口中声明的方法,代理类还会代理 java.lang.Object 中的hashCode、equals、toString三个方法
动态代理不足
- JDK动态代理只能对接口进行代理,不能对类进行代理,所以实现类中非接口声明的其他方法是无法被代理的
- JDK动态代理是基于反射调用的,执行效率较低