CGLIB经常使用使用示例:html
目标类java
public class Todo { public void doString(String desc) { System.out.println("doString: " + desc); } }
代理生成类 编程
public class ProxyInstance implements MethodInterceptor { public <T> T getInstance(Class<T> klass) { Enhancer enhancer = new Enhancer(); /* * 设置父类型,用于生成代理的子类 */ enhancer.setSuperclass(klass); /* * 设置实现Callback接口的实例对象 * 这里为MethodInterceptor对象 */ enhancer.setCallback(this); /* * create()用于建立代理类对象 * createClass()用于生成java字节码 */ return (T) enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("proxy before"); Object obj= methodProxy.invokeSuper(o, objects); System.out.println("proxy after"); return obj; } }
测试类app
public class Test { public static void main(String[] args) { String path = Test.class.getClassLoader().getResource("").getPath(); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path + "cglib"); Todo todo=new ProxyInstance().getInstance(Todo.class); todo.doString("------ nothing-------- "); } }
控制台框架
CGLIB debugging enabled, writing to '/E:/ideaspace/demo/demo/target/classes/cglib' proxy before doString: ------ nothing-------- proxy after
ASM(assembly):汇编的缩写,是java的一个字节码级别的编程框架,它能够动态生成Class的字节码文件,CGLIB以及Groovy等都是基于ASM实现动态语言特性的,在CGLIB的代理中主要用于生成类文件,实现方式为动态生成继承了目标类的代理类。maven
maven依赖ide
<dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>8.0.1</version> </dependency>
针对示例的目标类,生成类过多这里只展现主要代码:注意全部生成的类都会加载到JVM中测试
// 代理类相关的生成类this
Todo$$EnhancerByCGLIB$$939b412c$$FastClassByCGLIB$$b6c5bc7f.class Todo$$EnhancerByCGLIB$$939b412c.class // 代理类 Todo$$FastClassByCGLIB$$9f944a58.class
//其余类url
core/MethodWrapper$MethodWrapperKey$$KeyFactoryByCGLIB$$d45e49f7.class proxy/Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$7fb24d72.class
代理类描述
// 经过继承方式来实现代理,目标类成为了父类supper。经过继承实现也附带了继承的缺点,注意final类型和final方法的坑
public class Todo$$EnhancerByCGLIB$$939b412c extends Todo implements Factory {...}
针对方法的静态处理
static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; // 当前代理类 Class var0 = Class.forName("cn.tinyice.demo.proxy.cglib.Todo$$EnhancerByCGLIB$$939b412c"); Class var1; // 经过反射获取java.lang.Object声明的全部方法(不包括继承的方法,这里也不会有)中的equals、toString、hashCode、clone方法,准备重写 // 注意 这里 var1赋值 Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); // 对每一个方法获取其Method对象$Method以及代理对象$Proxy CGLIB$equals$1$Method = var10000[0]; CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1"); CGLIB$toString$2$Method = var10000[1]; CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2"); CGLIB$hashCode$3$Method = var10000[2]; CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3"); CGLIB$clone$4$Method = var10000[3]; CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4"); // 获取目标类用户方法doString的Method对象$Method以及代理对象$Proxy,注意这里var1赋值 CGLIB$doString$0$Method = ReflectUtils.findMethods(new String[]{"doString", "(Ljava/lang/String;)V"}, (var1 = Class.forName("cn.tinyice.demo.proxy.cglib.Todo")).getDeclaredMethods())[0]; CGLIB$doString$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "doString", "CGLIB$doString$0"); }
代理方法描述
public final void doString(String var1) { // 方法拦截器:从callBack中获取,这里为ProxyInstance 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$doString$0$Method, new Object[]{var1}, CGLIB$doString$0$Proxy); } else { // 不拦截方法则直接调用原方法 super.doString(var1); } }
到这里已经执行到了ProxyInstance#intercept,而后调用methodProxy.invokeSuper(o, objects);
public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { // 初始化MethodProxy实例,注意在调用时才初始化的 this.init(); // 获取FastClass MethodProxy.FastClassInfo fci = this.fastClassInfo; // 调用FastClassInfo的FastClass2的invoke,参数为方法签名下标,调用对象、方法参数 return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException var4) { throw var4.getTargetException(); } } private void init() { if (this.fastClassInfo == null) { synchronized(this.initLock) { if (this.fastClassInfo == null) { MethodProxy.CreateInfo ci = this.createInfo; MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo(); // fastClass1 生成 ,c1为MethodProxy.create的第一个参数,目标类 // 生成类名称前缀=参数2的类名称+$$FastClassByCGLIB$$Integer.toHexString(hashCode) fci.f1 = helper(ci, ci.c1); // fastClass2生成,c2为MethodProxy.create的第二个参数,代理类Class fci.f2 = helper(ci, ci.c2); // 调用getIndex获取下标,参数为方法签名,两者一一映射,这里使用switch语句来提升效率 fci.i1 = fci.f1.getIndex(this.sig1); fci.i2 = fci.f2.getIndex(this.sig2); // 实例构建完毕 this.fastClassInfo = fci; this.createInfo = null; } } } }
法拦截器中的调用 : fci.f2.invoke(fci.i2, obj, args)
目标类$$EnhancerByCGLIB$$FastClassByCGLIB$$Integer.toHexString(hashCode).invoke(代理类的方法签名、传入代理对象,传入的方法参数)
调用时一样使用switch语句来调用对应的方法
总结:
经过Enhancer能够生成代理类,传入CallBack使代码执行时进入CallBack代码中,从而执行用户自定义代码;
用户自定义代码能够决定使用目标类调用仍是代理类调用
-
目标类使用$$FastClass1进行调用
-
代理类使用$$FastClass2进行调用
-
$$FastClass都在调用时触发生成
$$FastClass为动态生成类,经过index与方法签名一一映射产生switch case 语句来提升调用效率
代理类是经过继承方式实现,不能对final类和方法使用CGLIB代理。
所以 CGLB代理调用效率高,可是生成效率慢,至少3个文件,1个代理类,2个FastClass类。全部生成的类都会加载到JVM堆中。
原文地址:
CGLIB代理使用示例与底层原理解析