本文共 5370 字,大约阅读时间需要 17 分钟。
上篇博客介绍了JDK当中提供的动态代理实现方式,这篇主要是来介绍,通过CGLib工具实现的动态代理。
首先在maven项目当中添加jar包依赖
cglib cglib 3.1
接下来我们可以直接编写具体的实现类,不需要提供接口
public class HelloServiceImpl { public void sayHello() { System.out.println("hello eakonzhao"); }}
接下来编写自定义的代理类,需要继承MethodInterceptor
public class HelloMethodInterceptor implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("Before:" + method.getName()); Object object = methodProxy.invokeSuper(o,objects); System.out.println("After:" + method.getName()); return object; }}
测试类
public class Client { public static void main(String[] args) { // 设置动态代理生成的class文件路径 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\Users\\Administrator\\Desktop\\"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HelloServiceImpl.class); enhancer.setCallback(new HelloMethodInterceptor()); HelloServiceImpl helloService = (HelloServiceImpl)enhancer.create(); helloService.sayHello(); }}执行结果Before:sayHellohello eakonzhaoAfter:sayHello
我们发现这个CGLib动态代理生成了三个class文件
这个类继承了我们前面设置的HelloServiceImpl类,实现了Factory接口
我们主要看HelloServiceImpl$$EnchancerByCGLIB$$这个类,这个类当中生成了一个final的类型的sayHello方法,还有一个CGLIB$sayHello$0
public final void sayHello() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy); } else { super.sayHello(); } } final void CGLIB$sayHello$0() { super.sayHello(); }
这里的this.CGLIB$CALLBACK_0就是我们传递进去的HelloMethodInterceptor对象,如果为null的话,可以看到是直接调用父类的sayHello方法,否则就调用HelloMethodInterceptor当中的intercept方法。
当我们调用自定义的HelloMethodInterceptor方法的intercept时,会执行我们添加进去的逻辑,调用MethodProxy.invokeSuper方法,来调用我们自己类的实现方法,接下来看MethodProxy当中invokeSuper的实现
public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { this.init(); MethodProxy.FastClassInfo fci = this.fastClassInfo; return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException var4) { throw var4.getTargetException(); } }
我们来看下init方法
private void init() { if(this.fastClassInfo == null) { Object var1 = this.initLock; synchronized(this.initLock) { if(this.fastClassInfo == null) { MethodProxy.CreateInfo ci = this.createInfo; MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo(); fci.f1 = helper(ci, ci.c1); fci.f2 = helper(ci, ci.c2); fci.i1 = fci.f1.getIndex(this.sig1); fci.i2 = fci.f2.getIndex(this.sig2); this.fastClassInfo = fci; this.createInfo = null; } } } }
这一步主要是完成对FastClassInfo的初始化操作,关于FastClassInfo,其结构如下
private static class FastClassInfo { FastClass f1; FastClass f2; int i1; int i2; private FastClassInfo() { } }
f1主要保存的是代理对象的类,即HelloMethodInterceptor对象,f2是被代理对象的类信息即HelloServiceImpl,i1是代理类方法签名索引,i2是被代理类方法签名索引
FastClass的实现类就是我们上面截图当中生成的
public int getIndex(Signature var1) { String var10000 = var1.toString(); switch(var10000.hashCode()) { case -1725733088: if(var10000.equals("getClass()Ljava/lang/Class;")) { return 7; } break; case -1026001249: if(var10000.equals("wait(JI)V")) { return 2; } break; case 243996900: if(var10000.equals("wait(J)V")) { return 3; } break; case 946854621: if(var10000.equals("notifyAll()V")) { return 9; } break; case 1116248544: if(var10000.equals("wait()V")) { return 1; } break; case 1535311470: if(var10000.equals("sayHello()V")) { return 0; } break; case 1826985398: if(var10000.equals("equals(Ljava/lang/Object;)Z")) { return 4; } break; case 1902039948: if(var10000.equals("notify()V")) { return 8; } break; case 1913648695: if(var10000.equals("toString()Ljava/lang/String;")) { return 5; } break; case 1984935277: if(var10000.equals("hashCode()I")) { return 6; } } return -1; }
生成的getIndex方法,根据方法签名的hash值来返回索引,sayHello方法返回索引为0,
在进行方法调用时,根据索引调用相应的方法。CGLib是在invoke时,会生成FastClass对象,我们在调用的时候,其实调用的是已经生成好的方法,
可以看到其实调用的被代理类的方法,即CGLIB$sayHello$0 方法,而fci.f1对象的其实是HelloServiceImpl$$EnchancerByCGLIB$$当中的 sayHello方法。
对比CGLib和JDK当中的动态代理
JDK动态代理:
1、被代理对象必须实现一个接口,而CGLib则不需要,可以是单独一个类,也可以是一个接口的实现
2、JDK动态代理生成的文件比较简单,而CGLib生成的字节码文件更复杂,
3、在方法调用上,JDK采用反射的方式进行方法调用,而CGLib是直接调用生成好的方法,执行效率更高。