Cglib动态代理
Cglib动态代理
使用Cglib提供的动态代理功能,我们需要在项目中引入Cglib依赖。
maven依赖配置文件中添加Cglib:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version> </dependency>
gradle依赖配置文件中添加Cglib:
compile group: 'cglib', name: cglib, version: '3.2.5'
Cglib是Code Generator Library的缩写,即代码生成库。Cglib通过使用ASM框架操作字节码控制对象的访问,提供方法拦截操作,它可以在运行期扩展Java类与实现Java接口,不仅可以实现基于接口的动态代理也可实现基于子类的动态代理。
我们还是以打印第三方API调用耗时为例,只是使用Cglib生成动态代理类我们不需要定义接口,可直接创建代理HttpRequestTemplateImpl类的动态代理类实例。如代码清单6-15所示。
代码清单6-15 使用cglib创建HttpRequestTemplateImpl代理对象
public class CglibAop { static { // 代理类class文件存入本地磁盘 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/tmp"); } public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HttpRequestTemplateImpl.class); enhancer.setCallback(new HttpRequestMethodInterceptor()); HttpRequestTemplateImpl requestTemplate = (HttpRequestTemplateImpl) enhancer.create(); } }
代码清单6-15中,静态代码块的作用是为Cglib添加配置,让Cglib将动态生成的代理类的class字节码输出到文件,文件保存在tmp目录下。main方法中,首先创建一个增强器Enhancer,调用Enhancer实例的setSuperclass方法写入需要被代理的类,调用Enhancer实例的setCallback方法添加方法拦截器,最后调用Enhancer实例的create方法创建代理类实例。
HttpRequestMethodInterceptor方法拦截器的实现如代码清单6-16所示。
代码清单6-16 HttpRequestMethodInterceptor方法拦截器
public class HttpRequestMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { long startMs = System.currentTimeMillis(); try { return methodProxy.invokeSuper(obj,objects); } finally { long cntMs = System.currentTimeMillis() - startMs; System.out.println(method.getName() + "方法的执行耗时为" + cntMs + "毫秒"); } } }
Cglib为HttpRequestTemplateImpl类生成的动态代理类如代码清单6-17所示。
代码清单6-17 cglib生成的HttpRequestTemplateImpl代理类
public class HttpRequestTemplateImpl$$EnhancerByCGLIB$$303ac1f7 extends HttpRequestTemplateImpl implements Factory { ...... private static final Method CGLIB$doGet$0$Method; private static final MethodProxy CGLIB$doGet$0$Proxy; ...... final HttpResponse CGLIB$doGet$0(HttpRequest var1) { return super.doGet(var1); } public final HttpResponse doGet(HttpRequest var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null ? (HttpResponse)var10000.intercept(this, CGLIB$doGet$0$Method, new Object[]{var1}, CGLIB$doGet$0$Proxy) : super.doGet(var1); } final HttpResponse CGLIB$doPost$1(HttpRequest var1) { return super.doPost(var1); } public final HttpResponse doPost(HttpRequest var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null ? (HttpResponse)var10000.intercept(this, CGLIB$doPost$1$Method, new Object[]{var1}, CGLIB$doPost$1$Proxy) : super.doPost(var1); } ...... }
从Cglib生成的代理类可以看出,Cglib通过继承父类并重写父类的方法实现方法拦截。代理类重写父类的方法,先判断方法拦截器是否存在,存在则调用方法拦截器的invoker方法,否则直接调用父类的方法。
与基于接口的实现不同,因为接口本身没有实现方法,需要给方法拦截处理器传递一个接口的实现类对象,在方法拦截处理器中调用这个对象的方法。通过继承被代理类生成的动态代理类,代理类重写父类方法拦截父类方法的调用,我们不需要给方法拦截器传递一个父类实例的引用,只需要在方法拦截器中能调用父类的方法。
想要在方法拦截器中调用代理对象的父类方法,只能通过在代理类中使用super关键字调用父类的方法。因此,在为代理类重写父类方法时,也要为代理类添加一个调用父类方法的额外方法。从代码清单6-17中我们可以看出,Cglib为实现在方法拦截器中能够调用父类的方法,也为每一个重写的方法都生成了一个额外的方法,如为doGet方法生成一个CGLIB$doGet$0方法,为doPost方法生成一个CGLIB$doPost$1方法。