动态代理
1 Overview
1.1 InvocationHandler
1.2 Proxy
2 示例
1 Overview
从 Java 1.3 开始,JDK 在 java.lang.reflect 包中加入了 Proxy 类和 InvocationHandler 接口。Proxy 是一个功能强大的类,它可以用来动态地创建指定接口的代理实现,并把对接口方法调用发送至 InvocationHandler 的实现类执行。
Spring 的 AOP 框架即基于此技术实现。
1.1 InvocationHandler
InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?
- proxy: 指代我们所代理的那个真实对象
- method: 指代的是我们所要调用真实对象的某个方法的Method对象
- args: 指代的是调用真实对象某个方法时接受的参数
1.2 Proxy
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
JDK 中注释如下
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义
- loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
- interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
- h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
2 示例
-
创建接口
-
创建实现类
-
定义动态代理类,这个可以做成通用类
-
最后,完成最终应用场景
-
运行,输出结果如下所示:
com.sun.proxy.$Proxy0 Before invoke Method: public abstract void lld.test.reflect.Hello.sayHi(java.lang.String) Hello, Lindong After invoke
首先,我们看到打印出来的类名是 $Proxy0,而不是 Hello 或 InvocationHandler 的实现类,但却又可以转化为 Hello 接口实现。原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是 Hello 类型,所以就可以将其转化为Subject类型了。
同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
我们看到我们的这个 method 对象是这样的:
Method: public abstract void lld.test.reflect.Hello.sayHi(java.lang.String)
正好就是我们的 Hello 接口中的方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。