cglib
1.什么是cglib
CGLIB(Code Generation Library)是一个开源项目。
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
2.何如使用
导入maven jar包
1 2 3 4 5 6
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
|
简单编写一个UserService的test方法,用来输出hello world,用来修改类中方法的执行前后事件
1 2 3 4 5 6
| public class UserService { public void test(){ System.out.println("hello world"); } }
|
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
|
public class TestClass {
public static void main(final String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before method run..."); Object result = methodProxy.invokeSuper(o, args); System.out.println("after method run..."); return result; } }); UserService userService = (UserService) enhancer.create(); userService.test(); } }
|
以上方法可以看到修改了TestClass类中test方法,在输出hello world之前调用Enhancer回调方法的intercept,在执行test前后,输出语句
代码执行结果为
1 2 3
| before method run... hello world after method run...
|
3.能否代理接口?
我们知道了cglib可以代理类,那么是否可以代理接口?我们写一个接口,并且创建一个方法去实现接口
1 2 3 4 5
| public interface UserService {
public void test(); }
|
接下来一样的去代理这个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class MainInterface {
public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("切面逻辑..."); return null; } }); UserService userService = (UserService) enhancer.create(); userService.test(); } }
|
输出结果为
答案是cglib和jdk里面的动态代理相似,可以进行写入。
4.如何实现的?
cglib可以动态产生一个代理对象,重写内部代码,那么查看他的代理对象类新增内容才能搞明白cglib是如何动态代理的。
动态产生了代理对象,证明我们底层的class类已经被cglib动态生成一个修改后的class类,需要查看生成的class类可以在jvm里加入命令

生成的class文件

代理类最终样子
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
package com.brew;
import java.lang.reflect.Method; import net.sf.cglib.core.ReflectUtils; import net.sf.cglib.core.Signature; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
public class UserService$$EnhancerByCGLIB$$5320231d extends UserService implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$test$0$Method; private static final MethodProxy CGLIB$test$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$equals$1$Method; private static final MethodProxy CGLIB$equals$1$Proxy; private static final Method CGLIB$toString$2$Method; private static final MethodProxy CGLIB$toString$2$Proxy; private static final Method CGLIB$hashCode$3$Method; private static final MethodProxy CGLIB$hashCode$3$Proxy; private static final Method CGLIB$clone$4$Method; private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.brew.UserService$$EnhancerByCGLIB$$5320231d"); Class 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()); 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"); CGLIB$test$0$Method = ReflectUtils.findMethods(new String[]{"test", "()V"}, (var1 = Class.forName("com.brew.UserService")).getDeclaredMethods())[0]; CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0"); }
final void CGLIB$test$0() { super.test(); } public final void test() { 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$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy); } else { super.test(); } }
.... .... .... }
|
调用原理,cglib产生的代理类,继承了UserService并且重写了test方法,
在调用test前拿到拦截器的callback方法,并且调用拦截器的intercept方法,内部也能看到toString equles等常用方法,代理类所产生的方法,也是根据UserService内部的方法来产生。
如何赋值CallBack
在test方法中有一个==CGLIB$BIND_CALLBACKS==方法 这个方法调用了
==CGLIB$THREAD_CALLBACKS.get()== 并且这个==CGLIB$THREAD_CALLBACKS==
为ThreadLocal方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private static final void CGLIB$BIND_CALLBACKS(Object var0) { UserService$$EnhancerByCGLIB$$5320231d var1 = (UserService$$EnhancerByCGLIB$$5320231d)var0; if (!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if (var10000 == null) { var10000 = CGLIB$STATIC_CALLBACKS; if (var10000 == null) { return; } }
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; }
}
|
而ThreadLocal newInstance内Cglib提供的Factory提供的。
而他们都是通过enhancer.create()方法初始化调用的。
1 2 3 4 5 6 7
| private Object createHelper() { this.preValidate(); Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID); this.currentKey = key; Object result = super.create(key); return result; }
|
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
| protected Object create(Object key) { try { ClassLoader loader = this.getClassLoader(); Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE; AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader); if (data == null) { Class var5 = AbstractClassGenerator.class; synchronized(AbstractClassGenerator.class) { cache = CACHE; data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader); if (data == null) { Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache); data = new AbstractClassGenerator.ClassLoaderData(loader); newCache.put(loader, data); CACHE = newCache; } } }
this.key = key; Object obj = data.get(this, this.getUseCache()); return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj); } catch (RuntimeException var9) { throw var9; } catch (Error var10) { throw var10; } catch (Exception var11) { throw new CodeGenerationException(var11); } }
|
剩下的内容都能在动态编译后的class文件查看调用方法
5.MethodProxy使用
MethodProxy可以代理执行方法,在代码中有一行
1 2 3 4 5 6 7 8 9 10 11
| enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before method run..."); Object result1 = methodProxy.invoke(userSerivce, args); Object result2 = methodProxy.invokeSuper(o, args); System.out.println("after method run..."); return result; } });
|
这两个方法一种是执行动态class文件中的test方法和CGLIB$test$0(分别代表加强与非加强)
注意:invoke方法不能在使用Object o,不然会出现循环调用,堆栈溢出的问题。
JDK动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public class JDKDemo { public static void main(String[] args) { UserService userService = (UserService) Proxy.newProxyInstance(JDKDemo.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("test"); return null; } }); userService.test(); } }
|
ASM & javassist
javassist使用全解析
Java 字节码以二进制的形式存储在 .class 文件中,每一个 .class 文件包含一个 Java 类或接口。Javaassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式。
本地项目使用示例
飞书jar包内使用okhttp的方式请求接口,需要使okhttp兼容ssl方法需要重写
1 2 3
| public static OkHttpClient create(long connectionTimeout, long socketTimeout, TimeUnit timeUnit) { return (new Builder()).connectTimeout(connectionTimeout, timeUnit).readTimeout(socketTimeout, timeUnit).build(); }
|
使用javassist动态加载可以解决
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
| String clsName = "com.larksuite.oapi.core.api.tools.OKHttps";
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(clsName);
CtMethod m = cc.getDeclaredMethod("create");
m.setBody("{ return cn.anicert.config.MHostnameVerifier.build;}");
CtClass ctClass = cp.makeClass("com.larksuite.oapi.core.api.tools.OKHttps");
Class<?> aClass = cc.toClass(Thread.currentThread().getContextClassLoader(),null);
|