23.1 静态代理
我们先介绍它的用法,然后介绍实现原理,最后分析它的优点。
23.2.1 用法
在静态代理中,代理类是直接定义在代码中的,在动态代理中,代理类是动态生成的,怎么动态生成呢?我们用动态代理实现前面的例子,如代码清单23-2所示。
代码清单23-2 使用Java SDK实现动态代理示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class SimpleJDKDynamicProxyDemo { static interface IService { public void sayHello(); } static class RealService implements IService { @Overridepublic void sayHello() { System.out.println("hello"); } } static class SimpleInvocationHandler implements InvocationHandler { private Object realObj; public SimpleInvocationHandler(Object realObj) { this.realObj = realObj; } @Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable { System.out.println("entering " + method.getName()); Object result = method.invoke(realObj, args); System.out.println("leaving " + method.getName()); return result; } } public static void main(String[] args) { IService realService = new RealService(); IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), new Class<? >[] {IService.class },new SimpleInvocationHandler(realService)); proxyService.sayHello(); } }
|
代码看起来更为复杂了,这有什么用呢?别着急,我们慢慢解释。IService和Real-Service的定义不变,程序的输出也没变,但代理对象proxyService的创建方式变了,它使用java.lang.reflect包中的Proxy类的静态方法newProxyInstance来创建代理对象,这个方法的声明如下:
1 2
| public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h)
|
它有三个参数,具体如下。
1)loader表示类加载器,下一章我们会单独探讨,例子使用和IService一样的类加载器。
2)interfaces表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中只有一个IService。
3)h的类型为InvocationHandler,它是一个接口,也定义在java.lang.reflect包中,它只定义了一个方法invoke,对代理接口所有方法的调用都会转给该方法。
newProxyInstance的返回值类型为Object,可以强制转换为interfaces数组中的某个接口类型。这里我们强制转换为了IService类型,需要注意的是,它不能强制转换为某个类类型,比如RealService,即使它实际代理的对象类型为RealService。
SimpleInvocationHandler实现了InvocationHandler,它的构造方法接受一个参数realObj表示被代理的对象,invoke方法处理所有的接口调用,它有三个参数:
1)proxy表示代理对象本身,需要注意,它不是被代理的对象,这个参数一般用处不大。
2)method表示正在被调用的方法。
3)args表示方法的参数。
在SimpleInvocationHandler的invoke实现中,我们调用了method的invoke方法,传递了实际对象realObj作为参数,达到了调用实际对象对应方法的目的,在调用任何方法前后,我们输出了跟踪调试语句。需要注意的是,不能将proxy作为参数传递给method. invoke,比如:
1
| Object result = method.invoke(proxy, args);
|
上面的语句会出现死循环,因为proxy表示当前代理对象,这又会调用到SimpleIn-vocationHandler的invoke方法。
23.2.2 基本原理
看了上面的介绍是不是更晕了,没关系,看下Proxy.newProxyInstance的内部就理解了。代码清单23-2中创建proxyService的代码可以用如下代码代替:
1 2 3 4 5 6
| Class<? > proxyCls = Proxy.getProxyClass(IService.class.getClassLoader(), new Class<? >[] { IService.class }); Constructor<? > ctor = proxyCls.getConstructor( new Class<? >[] { InvocationHandler.class }); InvocationHandler handler = new SimpleInvocationHandler(realService); IService proxyService = (IService) ctor.newInstance(handler);
|
分为三步:
1)通过Proxy.getProxyClass创建代理类定义,类定义会被缓存;
2)获取代理类的构造方法,构造方法有一个InvocationHandler类型的参数;
3)创建InvocationHandler对象,创建代理类对象。
Proxy.getProxyClass需要两个参数:一个是ClassLoader;另一个是接口数组。它会动态生成一个类,类名以$Proxy开头,后跟一个数字。对于上面的例子,动态生成的类定义如代码清单23-3所示,为简化起见,我们忽略了异常处理的代码。
代码清单23-3 Java SDK动态生成的代理类示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| final class $Proxy0 extends Proxy implementsSimpleJDKDynamicProxyDemo.IService { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { return((Boolean) this.h.invoke(this, m1,new Object[] {paramObject })).booleanValue(); } public final void sayHello() { this.h.invoke(this, m3, null); } public final String toString() { return (String) this.h.invoke(this, m2, null); } public final int hashCode() { return ((Integer) this.h.invoke(this, m0, null)).intValue(); } static { m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] {Class.forName("java.lang.Object") }); m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService").getMethod("sayHello", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } }
|
$Proxy0的父类是Proxy,它有一个构造方法,接受一个InvocationHandler类型的参数,保存为了实例变量h, h定义在父类Proxy中,它实现了接口IService,对于每个方法,如sayHello,它调用InvocationHandler的invoke方法,对于Object中的方法,如hash-Code、equals和toString, $Proxy0同样转发给了InvocationHandler。
可以看出,这个类定义本身与被代理的对象没有关系,与InvocationHandler的具体实现也没有关系,而主要与接口数组有关,给定这个接口数组,它动态创建每个接口的实现代码,实现就是转发给InvocationHandler,与被代理对象的关系以及对它的调用由InvocationHandler的实现管理。
我们是怎么知道$Proxy0的定义的呢?对于Oracle的JVM,可以配置java的一个属性得到,比如:
1 2
| java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true shuo.laoma.dynamic.c86. SimpleJDKDynamicProxyDemo
|
以上命令会把动态生成的代理类$Proxy0保存到文件$Proxy0.class中,通过一些反编译器工具比如JD-GUI(http://jd.benow.ca/ )就可以得到源码。
理解了代理类的定义,后面的代码就比较容易理解了,就是获取构造方法,创建代理对象。
23.2.3 动态代理的优点
相比静态代理,动态代理看起来麻烦了很多,它有什么好处呢?使用动态代理,可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每个被代理的类型都创建一个静态代理类。看个简单的示例,如代码清单23-4所示。
代码清单23-4 通用的动态代理类示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class GeneralProxyDemo { static interface IServiceA { public void sayHello(); } static class ServiceAImpl implements IServiceA { @Override public void sayHello() { System.out.println("hello"); } } static interface IServiceB { public void fly(); } static class ServiceBImpl implements IServiceB { @Override public void fly() { System.out.println("flying"); } } static class SimpleInvocationHandler implements InvocationHandler { private Object realObj; public SimpleInvocationHandler(Object realObj) { this.realObj = realObj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("entering " + realObj.getClass() .getSimpleName() + "::" + method.getName()); Object result = method.invoke(realObj, args); System.out.println("leaving " + realObj.getClass() .getSimpleName() + "::" + method.getName()); return result; } } private static <T> T getProxy(Class<T> intf, T realObj) { return (T) Proxy.newProxyInstance(intf.getClassLoader(), new Class<? >[] { intf }, new SimpleInvocationHandler(realObj)); } public static void main(String[] args) throws Exception { IServiceA a = new ServiceAImpl(); IServiceA aProxy = getProxy(IServiceA.class, a); aProxy.sayHello(); IServiceB b = new ServiceBImpl(); IServiceB bProxy = getProxy(IServiceB.class, b); bProxy.fly(); } }
|
在这个例子中,有两个接口IServiceA和IServiceB,它们对应的实现类是Service-AImpl和ServiceBImpl,虽然它们的接口和实现不同,但利用动态代理,它们可以调用同样的方法getProxy获取代理对象,共享同样的代理逻辑SimpleInvocationHandler,即在每个方法调用前后输出一条跟踪调试语句。程序输出为:
1 2 3 4 5 6
| entering ServiceAImpl::sayHello hello leaving ServiceAImpl::sayHello entering ServiceBImpl::fly flying leaving ServiceBImpl::fly
|