
正文一. 代理模式在学习动态代理之前先回顾一下设计模式中的代理模式。代理模式定义为给被代理对象提供一个代理对象以控制对被代理对象的访问即访问对象不适合或者不能直接引用被代理对象时代理对象作为访问对象和被代理对象之间的中介。代理模式中有三种角色分别为抽象主题AbstractSubject真实主题RealSubject和代理Proxy三种角色含义如下表所示。角色含义抽象主题AbstractSubject通过接口或者抽象类声明真实主题和代理需要实现的业务方法。真实主题RealSubject实现了抽象主题中的业务方法是代理所代表的真实对象是最终要引用的对象。代理Proxy实现了抽象主题提供了与真实主题相同的方法其内部含有对真实主题的引用可以访问控制或扩展真实主题的功能。代理模式的三种角色的关系用类图表示如下。二. 静态代理根据代理模式中的代理类的字节码文件的创建时机可以将代理分为静态代理和动态代理静态代理在程序运行前代理类的字节码文件就已经存在而动态代理则是在程序运行期间JVM通过反射机制为代理类生成字节码文件。本小节以一个例子对静态代理进行学习。定义抽象主题如下所示。public interface TestServiceA { void executeTestA(); void submitTestA(); } public interface TestServiceB { void executeTestB(); void submitTestB(); }上述定义了两个接口作为抽象主题下面定义一个真实主题来实现抽象主题如下所示。public class RealObject implements TestServiceA, TestServiceB { Override public void executeTestA() { System.out.println(Test A execute.); } Override public void submitTestA() { System.out.println(Test A submit.); } Override public void executeTestB() { System.out.println(Test B execute.); } Override public void submitTestB() { System.out.println(Test B submit.); } }再定义一个代理类如下所示。public class ProxyObject implements TestServiceA, TestServiceB { private RealObject realObject; public ProxyObject(RealObject realObject) { this.realObject realObject; } Override public void executeTestA() { before(); realObject.executeTestA(); after(); } Override public void submitTestA() { before(); realObject.submitTestA(); after(); } Override public void executeTestB() { before(); realObject.executeTestB(); after(); } Override public void submitTestB() { before(); realObject.submitTestB(); after(); } private void before() { System.out.println(Begin to do.); } private void after() { System.out.println(Finish to do.); } }可以看到真实主题RealObject和代理ProxyObject均实现了抽象主题同时代理ProxyObject还持有真实主题RealObject的引用因此需要通过ProxyObject才能访问到RealObject同时ProxyObject在执行RealObject的方法时还可以执行一些额外的逻辑来扩展RealObject的功能。编写一个客户端程序如下所示。public class ClientOne { public static void main(String[] args) { RealObject realObject new RealObject(); ProxyObject proxyObject new ProxyObject(realObject); proxyObject.executeTestA(); proxyObject.submitTestA(); proxyObject.executeTestB(); proxyObject.submitTestB(); } }运行结果如下所示。三. JDK动态代理思考一下在第二小节中的静态代理在实际使用中存在什么不足这里归纳如下。如果让一个代理类代理多个被代理类那么会导致代理类变得过大如果每个被代理类都对应一个代理类那么会导致代理类变得过多由于被代理类和代理类都需要实现相同的接口当接口定义的方法增加或减少时被代理类和代理类需要一起修改不易于代码维护。上述静态代理存在的问题可以由动态代理来解决即在程序运行期间才决定代理类的生成。下面先根据一个基于JDK动态代理的例子来说明动态代理的使用方法然后再基于源码分析JDK动态代理的实现机制以及为什么可以动态的生成代理类。JDK动态代理主要是基于两个类java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler所有基于JDK动态代理生成的代理类均会继承于Proxy同时代理类会持有InvocationHandler的引用而InvocationHandler中会持有被代理类的引用因此可以将InvocationHandler理解为代理类与被代理类的中介。首先创建一个类实现InvocationHandler接口如下所示。public class TestInvocationHandler implements InvocationHandler { private Object realObject; public TestInvocationHandler(Object realObject) { this.realObject realObject; } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object invokeResult method.invoke(realObject, args); after(); return invokeResult; } private void before() { System.out.println(Begin to do.); } private void after() { System.out.println(Finish to do.); } }如上所示TestInvocationHandler实现了InvocationHandler接口同时TestInvocationHandler中有一个名为realObject的成员变量该变量就是被代理类当代理类执行代理方法时就会通过TestInvocationHandler来调用被代理类的方法同时TestInvocationHandler中还可以自己定义一些方法来实现功能扩展在上面例子中就定义了before()和after()两个方法分别用于在被代理类方法执行前和执行后做一些事情。创建好了TestInvocationHandler之后就可以开始创建动态代理类了其中被代理类还是沿用第二小节中的RealObject创建动态代理类的逻辑如下所示。public class ClientTwo { public static void main(String[] args) { // 保存动态代理类的字节码文件 System.getProperties().setProperty( sun.misc.ProxyGenerator.saveGeneratedFiles, true); // 创建被代理类 RealObject realObject new RealObject(); // 获取被代理类的类加载器 ClassLoader classLoader realObject.getClass() .getClassLoader(); // 获取被代理类实现的接口的Class对象 Class?[] interfaces realObject.getClass() .getInterfaces(); // 以被代理类作为入参创建InvocationHandler InvocationHandler invocationHandler new TestInvocationHandler(realObject); // 通过调用Proxy的newProxyInstance()方法创建动态代理对象 Object proxyInstance Proxy.newProxyInstance( classLoader, interfaces, invocationHandler); ((TestServiceA) proxyInstance).executeTestA(); ((TestServiceA) proxyInstance).submitTestA(); ((TestServiceB) proxyInstance).executeTestB(); ((TestServiceB) proxyInstance).submitTestB(); } }运行上述程序执行结果如下所示。在工程目录/com/sun/proxy下查看生成的代理类的字节码文件反编译如下所示。public final class $Proxy0 extends Proxy implements TestServiceA, TestServiceB { private static Method m1; private static Method m3; private static Method m2; private static Method m6; private static Method m5; private static Method m0; private static Method m4; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void executeTestA() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void executeTestB() throws { try { super.h.invoke(this, m6, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void submitTestB() throws { try { super.h.invoke(this, m5, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void submitTestA() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 Class.forName(java.lang.Object).getMethod(equals, Class.forName(java.lang.Object)); m3 Class.forName(cn.sakura.sacrifice.dynamic.TestServiceA).getMethod(executeTestA); m2 Class.forName(java.lang.Object).getMethod(toString); m6 Class.forName(cn.sakura.sacrifice.dynamic.TestServiceB).getMethod(executeTestB); m5 Class.forName(cn.sakura.sacrifice.dynamic.TestServiceB).getMethod(submitTestB); m0 Class.forName(java.lang.Object).getMethod(hashCode); m4 Class.forName(cn.sakura.sacrifice.dynamic.TestServiceA).getMethod(submitTestA); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }可以看到生成的代理类继承于Proxy同时也实现了被代理类实现的接口当代理类执行代理方法时会通过其继承于Proxy的InvocationHandler来调用到被代理类的真实方法至此JDK动态代理的一个例子就介绍到这里。现在看一下Proxy.newProxyInstance()方法做了哪些事情来搞明白为什么可以动态的生成代理类方法源码如下所示。CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class?[] intfs interfaces.clone(); final SecurityManager sm System.getSecurityManager(); if (sm ! null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } // 生成代理类的Class对象 Class? cl getProxyClass0(loader, intfs); try { if (sm ! null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 获取代理类的构造器 final Constructor? cons cl.getConstructor(constructorParams); final InvocationHandler ih h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedActionVoid() { public Void run() { cons.setAccessible(true); return null; } }); } // 生成代理对象 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }getProxyClass0()方法会生成代理类的Class对象生成过的代理类的Class对象会缓存在Proxy的类变量proxyClassCache中所以getProxyClass0()方法会先在proxyClassCache中获取代理类Class对象如果获取不到则会通过ProxyClassFactory来生成代理类Class对象。ProxyClassFactory是Proxy的静态内部类其主要完成两件事情。生成代理类的字节码文件调用native方法defineClass0()来解析代理类的字节码文件并生成代理类的Class对象。ProxyClassFactory中生成代理类的字节码文件时是调用的ProxyGenerator的generateProxyClass()方法并且在生成字节码文件前会将Object的hashCode()equals()和toString()方法以及被代理类实现的接口所定义的方法添加到代理类的方法中。至此可以对JDK动态代理如何动态生成代理类进行如下的图示归纳。四. CGLIB动态代理在JDK动态代理中要求被代理类需要实现接口这一点限制了JDK动态代理的使用当被代理类未实现接口时想要动态生成代理类可以使用CGLIB动态代理使用CGLIB生成的代理类是被代理类的子类本小节将结合例子对CGLIB的使用进行说明。首先创建一个被代理类如下所示。public class RealService { public void execute(String flag) { System.out.println(Test flag execute.); } public void submit(String flag) { System.out.println(Test flag submit.); } }然后创建一个方法拦截器方法拦截器需要继承于MethodInterceptor用于在代理对象执行方法时进行拦截如下所示。public class TestInterceptor implements MethodInterceptor { /** * param o 代理对象 * param method 被代理对象的方法 * param objects 被代理对象的方法参数类型 * param methodProxy 被代理对象的方法的代理 */ Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object result methodProxy.invokeSuper(o, objects); after(); return result; } private void before() { System.out.println(Begin to do.); } private void after() { System.out.println(Finish to do.); } }上述方法拦截器会在每一个代理对象的方法执行时进行拦截然后依次执行before()方法被代理对象的方法和after()方法以达到对被代理对象的方法的增强效果同时intercept()方法的第一个参数是代理对象所以想要执行被代理对象的方法需要使用invokeSuper()。最后创建客户端程序来测试效果如下所示。public class ClientThree { public static void main(String[] args) { Enhancer enhancer new Enhancer(); enhancer.setSuperclass(RealService.class); enhancer.setCallback(new TestInterceptor()); Object proxyObject enhancer.create(); ((RealService) proxyObject).execute(cglib); ((RealService) proxyObject).submit(cglib); } }运行结果如下所示。CGLIB动态代理也能保存代理类的字节码文件只需要做如下设置。System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, 保存路径);现在通过IDEA反编译字节码文件之后可以查看生成的代理类下面截取一部分来说明代理方法的调用和增强如下所示。public class RealService$$EnhancerByCGLIB$$64276695 extends RealService implements Factory { ...... static void CGLIB$STATICHOOK1() { ...... CGLIB$execute$0$Proxy MethodProxy.create(var1, var0, (Ljava/lang/String;)V, execute, CGLIB$execute$0); ...... } ...... public final void execute(String var1) { MethodInterceptor var10000 this.CGLIB$CALLBACK_0; if (var10000 null) { CGLIB$BIND_CALLBACKS(this); var10000 this.CGLIB$CALLBACK_0; } if (var10000 ! null) { var10000.intercept(this, CGLIB$execute$0$Method, new Object[]{var1}, CGLIB$execute$0$Proxy); } else { super.execute(var1); } } ...... }查看反编译得到的代理类可以知道CGLIB动态代理生成的代理类是被代理类的子类以及当代理类调用方法时会通过MethodInterceptor来调用被代理类的方法和增强方法。至此CGLIB动态代理的例子介绍完毕相较于JDK动态代理CGLIB动态代理是通过字节码处理框架ASM来动态生成代理类的字节码文件并加载到JVM中下面是JDK动态代理和CGLIB动态代理的一个简单对比。JDK动态代理JDK动态代理中代理类调用被代理类的方法依赖InvocationHandler接口JDK动态代理要求被代理类需要实现一个或多个接口JDK动态代理是基于反射来动态生成代理类的字节码文件。CGLIB动态代理CGLIB动态代理中代理类调用被代理类的方法依赖MethodInterceptor接口CGLIB动态代理要求被代理类不能为final但不要求被代理类需要实现接口CGLIB动态代理无法为被代理类中的final方法进行代理CGLIB动态代理是基于ASM框架来动态生成代理类的字节码文件。总结本篇文章对代理设计模式静态代理JDK动态代理和CGLIB动态代理进行了讨论。静态代理实现最为简单在程序编译完成之后代理类的字节码文件已经生成可以直接被JVM加载到内存中使用效率高省去了动态代理中的生成字节码文件的时间但是缺点就是静态代理中通常一个代理类只代理一个被代理类如果被代理类过多会导致代理类也过多。动态代理可以解决代理类过多的问题其中JDK动态代理可以在程序运行期间基于反射来动态生成代理类的字节码文件但是要求被代理类实现接口这限制了JDK动态代理的使用场景而CGLIB动态代理不要求被代理类实现接口其底层是基于ASM框架来动态生成代理类的字节码文件CGLIB创建的代理类是被代理类的子类所以CGLIB动态代理要求被代理类不能是final的。