之前看了好几遍java教程关于动态代理的介绍,老觉得理解不够深入,所以今天特意参考API并结合实例,记录自己学习动态代理的心得。
所谓动态代理,我理解是在程序运行过程中动态创建接口的实例,这里的创建实例并不是直接使用new 构造器的方法实现,而是通过动态代理类代理生成。动态代理类是一个实现了一系列(一个或多个)接口的类。以此相关的还有两个概念:
- 代理接口:那些被动态代理类实现的接口;
- 代理实例:动态代理类的实例。
每一个动态代理类都有一个相关联的 invocation
handler (额,不知道该怎么翻译)对象,该对象实现了 InvocationHandler接口。 invocation
handler 有什么用呢,搁置着先,看看简单的动态代理是如何实现的。
java提供的Proxy(java.lang.reflect包下)类带有创建动态代理类或者代理类实例的静态方法,动态代理可以有以下两个实现(假如代理接口为ProxyInterface):
方法一:
//获得实现InvocationHandler接口的类的实例 InvocationHandler handler = new MyInvocationHandler(...); //创建动态代理类proxyClass,代理的接口为Foo Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class } ); //通过获取动态代理类的构造器,在使用构造器来创建对象,该对象就是实现了接口Foo的实例 Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });
方法二:
//通过Proxy的newProxyInstance方法直接生成代理实例 Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
由上可知,要实现代理,必须具备三个要素:1.实现了InvocationHandler接口的类的对象;2.代理接口的class loader;3.代理接口的Class对象的数组;
之前提到过,每一个动态代理类都有一个与之关联的InvocationHandler接口的类的对象,该对象的作用是什么呢?让我们继续完善以上代码,创建一个实现InvocationHandler接口的类,并且在动态代理类生成对象后,执行对象的方法,完整代码如下:
//代理接口的代码 package com.lauyu; public interface ProxyInterface { public void func(); } //动态代理类的代码 package com.lauyu; import java.lang.reflect.*; //实现InvocationHandler接口的类 class MyInvocationHandler implements InvocationHandler { //重写InvocationHandler的invoke方法 public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("---正在执行的方法:" + method); return null; } } public class DynamicProxyTest { public static void main(String[] args) { //创建hander实例 MyInvocationHandler myHandler = new MyInvocationHandler(); //通过Proxy.newProxyInstance方法创建代理实例,传入的参数有代理接口的class loader、代理接口数组、hander实例 ProxyInterface f = (ProxyInterface)Proxy.newProxyInstance(ProxyInterface.class.getClassLoader(), new Class[] { ProxyInterface.class }, myHandler); //执行代理实例的方法 f.func(); } }
可以看到如下输出:
---正在执行的方法:public abstract void com.lauyu.ProxyInterface.func()
程序的输出正是MyInvocationHandler类的invoke方法的方法体语句,也就是说,代理实例的调用自己的方法时,调用动作会被传递到与代理实例相关联的Hander对象的invoke方法,实际调用的是invoke方法。实际上,可以在invoke方法体内执行代理实例的方法,因为invoke方法的形参提供了执行代理实例的方法的全部信息。下面翻译一下invoke方法的形参的API解释:
完整方法签名是:Object invoke(Object proxy, Method method, Object[] args) throws Throwable
- proxy-有方法调用动作的代理实例
- method-Method类实例(反射相关的类),代表代理实例正在调用的接口的方法
- args-代理实例正在调用的接口的方法的参数,类型为数组
只要我们在invoke添加语句:method.invoke(proxy, args),就能调用代理实例的调用的方法,但是这样又会触发handler调用invoke方法,就会导致死循环,何况这里并没有实现代理接口的方法,所以调用意义不大。通常,都是在MyInvocationHandler类的invoke方法体内调用实现代理接口的类的方法(注意这里实现代理接口的类不是代理实例)。继续完善以上代码,创建一个实现ProxyInterface接口的类,并将实现类的对象传入MyInvocationHandler。
package com.lauyu; import java.lang.reflect.*; //接口实现类 class ProxyInterfaceInstance implements ProxyInterface { public void func() { System.out.println("---正在执行ProxyInterfaceInstance方法func"); } } //实现InvocationHandler接口的类 class MyInvocationHandler implements InvocationHandler { //MyInvocationHandler内部有个代理接口类型的属性 private ProxyInterface pii; public MyInvocationHandler(ProxyInterface pii2) { //这里在构造器中初始化代理接口类型的属性 this.pii = pii2; } //重写InvocationHandler的invoke方法 public Object invoke(Object proxy, Method method, Object[] args) throws Exception { System.out.println("---正在执行的方法:" + method); // method.invoke(pii); return null; } } public class DynamicProxyTest { public static void main(String[] args) { ProxyInterface pii = new ProxyInterfaceInstance(); //创建hander实例 MyInvocationHandler myHandler = new MyInvocationHandler(pii); //通过Proxy.newProxyInstance方法创建代理实例,传入的参数有代理接口的class loader、代理接口数组、hander实例 ProxyInterface f = (ProxyInterface)Proxy.newProxyInstance(ProxyInterface.class.getClassLoader(), new Class[]{ProxyInterface.class}, myHandler); //执行代理实例的方法 f.func(); } }
执行结果如下:
---正在执行的方法:public abstract void com.lauyu.ProxyInterface.func() ---正在执行ProxyInterfaceInstance方法func
可以看到,当代理实例调用接口的方法时,myHandler对象的invoke方法被执行,且在invoke内部还执行了ProxyInterface接口类的对象的方法func。这样调用func方法有一个很好地灵活性,就是如果想要在func方法执行之前或之后进行某种业务,只要在myHandler对象的invoke方法体内添加即可,而没有必要修改func方法,从而把软件耦合降低,事实上,AOP原理就是这样。