CGLIB浅析
| 本文总阅读量次本文承接自动态代理浅析这篇文章,对代理没有什么概念的同学建议先读下这篇文章。
本文打算从这几个方面来理解CGLIB怎样生成动态代理类的:
1. 使用Cglib动态代理
CGLIB的使用和JDK Proxy类似,不同的是CGLIB既可以指定接口,也可以直接代理未实现接口的普通类。
示例代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20Enhancer enhancer = new Enhancer();
LogInterceptor logInterceptor = new LogInterceptor();
// 设置超类,cglib是通过继承来实现的
enhancer.setSuperclass(UserDao.class);
enhancer.setCallback(logInterceptor);
/**
* 如果是实现指定接口,可以使用setInterfaces方法设置接口
*
*/
// enhancer.setInterfaces(new Class[]{Dao.class});
/**
* 如果使用createClass方法返回值为代理类的Class对象,需要设置CallbackType参数;当使用create方法时,CallbackType和callback都不为空时,两者类型必须相等。
* create方法返回的是代理类实例
* enhancer.setCallbackType(LogInterceptor.class);
*/
// 创建代理类
Dao dao = (Dao)enhancer.create();
dao.select();
// 方法拦截器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class LogInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// before();
Object result = methodProxy.invokeSuper(object, objects);
// after();
return result;
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}
// 实现类1
2
3
4
5
6
7
8
9
10
11public class UserDao implements Dao {
@Override
public void select() {
System.out.println("UserDao 查询 selectById");
}
@Override
public void update() {
System.out.println("UserDao 更新 update");
}
}
2. CGLIB生成动态代理类的原理解析
CGLIB,JDK proxy生成过程上大同小异,都是使用弱引用缓存已生成的Class对象,如果缓存中没有,则使用字节码技术生成动态代理类字节码和Class对象,只不过使用的字节码技术不太一样罢了。
下图是CGLIB动态代理生成代理类Class对象的流程:点击看大图
CGLIB动态代理基于ASM技术,使用ASM技术的部分就是图中标红的模块。
CGLIB动态代理如果没有特殊设置也会缓存已生成的动态代理类的Class对象。
上图是CGLIB缓存动态代理类的Class对象的结构。
CGLIB第一次生成动态代理类Class对象的方法调用流程:
3. CGLIB代理类调用方法原理解析
使用
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./")
可以输出生成的代理类的class文件。这里由于篇幅问题,只展示文章需要的内容,想看完整的代理类的字节码文件可以自己输出查看。
CGLIB针对一个目标类一般会生成3个动态代理类,1个UserDao$$EnhancerByCGLIB$$c00e2e9b extends UserDao implements Factory
,还有2个和调用有关的FastClass代理类UserDao$$EnhancerByCGLIB$$c00e2e9b$$FastClassByCGLIB$$ff71decc extends FastClass
,UserDao$$FastClassByCGLIB$$890e5f18 extends FastClass
UserDao$$EnhancerByCGLIB$$c00e2e9b类的静态初始化块:
1 | // 当前线程的缓存 |
当动态代理类调用update()方法(update是DAO接口内的方法)时,首先调用下面这个动态生成的方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* cglib使用Class对象创建对象实例(Enhancer.nextNewInstance())的时候,会反射调用CGLIB$SET_THREAD_CALLBACKS方法,将MethodInterceptor实例缓存在CGLIB$THREAD_CALLBACKS这个代理类的的ThreadLocal中,然后初始化动态代理类实例时,调用CGLIB$BIND_CALLBACKS方法将MethodInterceptor赋值给CGLIB$CALLBACK_0。
*/
public final void update() {
// 初始化动态代理类实例时,已经将MethodInterceptor赋值给了CGLIB$CALLBACK_0
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$update$0$Method, CGLIB$emptyArgs, CGLIB$update$0$Proxy);
} else {
super.update();
}
}
CGLIB$BIND_CALLBACKS方法将MethodInterceptor赋值给CGLIB$CALLBACK_0。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15private static final void CGLIB$BIND_CALLBACKS(Object var0) {
UserDao$$EnhancerByCGLIB$$c00e2e9b var1 = (UserDao$$EnhancerByCGLIB$$c00e2e9b)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
// 也就是说,初始化动态代理类实例时,最后如果var1.CGLIB$BOUND = true,则说明已经将MethodInterceptor赋值给了CGLIB$CALLBACK_0
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
现在调用到了MethodInterceptor的intercept方法了。
1 | public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { |
methodProxy,还记得动态代理类的static代码块么,在那时就已经初始化好了methodProxy:1
2
3
4
5
6
7
8
9public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
// 使用Signature对象封装方法名和返回值
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
// 将UserDAO和UserDAO的动态代理类的Class文件放入CreateInfo对象
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
return proxy;
}
好了知道methodProxy怎么来的,继续往下走,invokeSuper():
1 | public Object invokeSuper(Object obj, Object[] args) throws Throwable { |
根据索引调用方法,fci.i2的值为12,调用索引12的方法。
UserDao$$EnhancerByCGLIB$$c00e2e9b$$FastClassByCGLIB$$ff71decc动态代理类:
1 | public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException { |
访问流程图:
总结
无论是CGLIB还是JDK proxy都是为了控制对象的访问,但是怎样控制访问是CGLIB和JDK proxy思想上的最大区别,JDK proxy通过接口得到method对象后使用反射直接调用目标方法,CGLIB则是更复杂的多,CGLIB先是继承目标类,然后通过字节码技术生成代理方法(CGLIB$update$0)和update(上述例子中的方法,这里用来举例),然后通过方法代理(MethodProxy)和FastClass访问机制(通过方法名和返回值生成索引,调用时直接索引至目标方法)回调目标方法。
TIPS:其实这里还有很多想说的,但是总也感觉表达的总是不是那么的到位,所以暂且先这样了,等着后续再继续完善。
优点:
- 有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似CGLIB动态代理就没有这种限制。
- 只操作我们关心的类,不必为其他相关类增加工作量。
- 高性能,高性能体现方法的调用上,CGLIB的方法调用为FastClass机制,JDK Proxy为反射调用。
4. 一些源码
下面是一些比较核心的一些代码解析:
入口,AbstractGenerator中的create(key)方法,参数key=KeyFactory.newInstance()。KeyFactory也是CGLIB动态生成的。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// 参数key是Enhancer.KeyFactory(superclassname)的实例,这个KeyFactory类也是cglib动态生成的,当你new Enhancer()时,Enhancer会动态的生成该实例对象,superclassname就是上面的UserDAO。
protected Object create(Object key) {
try {
ClassLoader loader = this.getClassLoader();
// CACHE时WeakHashMap 第一层缓存的容器
Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
// 第一层缓存 key是classLoader
AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Class var5 = AbstractClassGenerator.class;
// 加锁意义在于CACHE时静态变量,属于该类,不是该实例对象,所以线程安全需要加锁。
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);
// 将第二层缓存容器放入第一层容器内,key为classloader
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
// 使用ClassLoaderData data获取Enhancer.EnhancerFactoryData对象,Enhancer.EnhancerFactoryData对象中是代理类的Class对象,也可以直接获取KeyFactory的Class对象
Object obj = data.get(this, this.getUseCache());
// firstInstance主要是针对KeyFactory生成的Class对象,例如接口方法生成的MethodWraper的代理类Class对象,nextInstance针对Enhancer实例生成的Enhancer.EnhancerFactoryData实例,实例中存储着真正的代理类的Class对象,如果生成的代理类中有接口方法,生成代理类时,会生成接口方法的MethodWrapper的代理类实例。
return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
} catch (Error | RuntimeException var9) {
throw var9;
} catch (Exception var10) {
throw new CodeGenerationException(var10);
}
}
一级缓存中的value,ClassLoaderData的构造方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public ClassLoaderData(ClassLoader classLoader) {
if (classLoader == null) {
throw new IllegalArgumentException("classLoader == null is not yet supported");
} else {
// 首先用弱引用封装classLoader,垃圾回收时可以直接回收
this.classLoader = new WeakReference(classLoader);
Function<AbstractClassGenerator, Object> load = new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
Class klass = gen.generate(ClassLoaderData.this);
// 这里需要注意gen是Enhancer实例,还是KeyFactory$Genertor实例,两者的wrapCachedClass完全不一样,使用的数据结构也不一样。Enhancer使用的数据结构是EnhancerFactoryData,KeyFactory$Genertor使用LoadingCache
return gen.wrapCachedClass(klass);
}
};
// this.generatedClasses的key是KeyFactory根据superclassName生成的key实例,将load放入LoadingCache,等下如果LoadingCache的map获取代理类为null,需要回调这个load重新生成代理类
this.generatedClasses = new LoadingCache(GET_KEY, load);
}
}
二级缓存的对象LoadingCache中的核心方法: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// key是Enhancer实例,或者是KeyFactory$Generator实例
public V get(K key) {
KK cacheKey = this.keyMapper.apply(key);
Object v = this.map.get(cacheKey);
return v != null && !(v instanceof FutureTask) ? v : this.createEntry(key, cacheKey, v);
}
protected V createEntry(final K key, KK cacheKey, Object v) {
boolean creator = false;
FutureTask task;
Object result;
if (v != null) {
task = (FutureTask)v;
} else {
task = new FutureTask(new Callable<V>() {
public V call() throws Exception {
// 回调AbstractGenerator.ClassLoaderData构造器中的load
return LoadingCache.this.loader.apply(key);
}
});
// 获取map中的value,如果是Enhancer.EnhancerFactoryData就直接返回,如果不是,继续往下获取Class对象
result = this.map.putIfAbsent(cacheKey, task);
// 如果等于null,需要重新生成
if (result == null) {
creator = true;
task.run();
} else {
if (!(result instanceof FutureTask)) {
return result;
}
task = (FutureTask)result;
}
}
try {
result = task.get();
} catch (InterruptedException var9) {
throw new IllegalStateException("Interrupted while loading cache item", var9);
} catch (ExecutionException var10) {
Throwable cause = var10.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException)cause;
}
throw new IllegalStateException("Unable to load cache item", cause);
}
// 新建的Class对象或者Enhancer.EnhancerFactoryData需要存起来
if (creator) {
this.map.put(cacheKey, result);
}
return result;
}
Enhancer中生成动态代理类的逻辑,使用了ASM技术: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
76public void generateClass(ClassVisitor v) throws Exception {
Class sc = this.superclass == null ? Object.class : this.superclass;
if (TypeUtils.isFinal(sc.getModifiers())) {
throw new IllegalArgumentException("Cannot subclass final class " + sc.getName());
} else {
List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
this.filterConstructors(sc, constructors);
List actualMethods = new ArrayList();
List interfaceMethods = new ArrayList();
final Set forcePublic = new HashSet();
// 将接口中的方法获取放入到forcePublic中,获取的其实是KeyFactory生成的MethodWrapper实例 select,}, void
// actualMethods是一个包含所有方法的数组,值类似public void com.zhongyp.advanced.proxy.cglib.UserDao.update()
getMethods(sc, this.interfaces, actualMethods, interfaceMethods, forcePublic);
// 获取到所有的方法及访问标识
List methods = CollectionUtils.transform(actualMethods, new Transformer() {
public Object transform(Object value) {
Method method = (Method)value;
int modifiers = 16 | method.getModifiers() & -1025 & -257 & -33;
if (forcePublic.contains(MethodWrapper.create(method))) {
// 如果接口中有相同的方法,
modifiers = modifiers & -5 | 1;
}
return ReflectUtils.getMethodInfo(method, modifiers);
}
});
// 下面就是ASM的操作逻辑了
// 参数v是ClassVisitor
ClassEmitter e = new ClassEmitter(v);
if (this.currentData == null) {
e.begin_class(46, 1, this.getClassName(), Type.getType(sc), this.useFactory ? TypeUtils.add(TypeUtils.getTypes(this.interfaces), FACTORY) : TypeUtils.getTypes(this.interfaces), "<generated>");
} else {
e.begin_class(46, 1, this.getClassName(), (Type)null, new Type[]{FACTORY}, "<generated>");
}
// 构造器信息
List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());
e.declare_field(2, "CGLIB$BOUND", Type.BOOLEAN_TYPE, (Object)null);
e.declare_field(9, "CGLIB$FACTORY_DATA", OBJECT_TYPE, (Object)null);
if (!this.interceptDuringConstruction) {
e.declare_field(2, "CGLIB$CONSTRUCTED", Type.BOOLEAN_TYPE, (Object)null);
}
e.declare_field(26, "CGLIB$THREAD_CALLBACKS", THREAD_LOCAL, (Object)null);
e.declare_field(26, "CGLIB$STATIC_CALLBACKS", CALLBACK_ARRAY, (Object)null);
if (this.serialVersionUID != null) {
e.declare_field(26, "serialVersionUID", Type.LONG_TYPE, this.serialVersionUID);
}
for(int i = 0; i < this.callbackTypes.length; ++i) {
e.declare_field(2, getCallbackField(i), this.callbackTypes[i], (Object)null);
}
e.declare_field(10, "CGLIB$CALLBACK_FILTER", OBJECT_TYPE, (Object)null);
if (this.currentData == null) {
this.emitMethods(e, methods, actualMethods);
this.emitConstructors(e, constructorInfo);
} else {
this.emitDefaultConstructor(e);
}
this.emitSetThreadCallbacks(e);
this.emitSetStaticCallbacks(e);
this.emitBindCallbacks(e);
if (this.useFactory || this.currentData != null) {
int[] keys = this.getCallbackKeys();
this.emitNewInstanceCallbacks(e);
this.emitNewInstanceCallback(e);
this.emitNewInstanceMultiarg(e, constructorInfo);
this.emitGetCallback(e, keys);
this.emitSetCallback(e, keys);
this.emitGetCallbacks(e);
this.emitSetCallbacks(e);
}
e.end_class();
}
}
FAQ
- CGLIB针对final方法怎么处理的呢?
不处理,因为继承了目标类,所以动态代理类实例可以直接调用目标类的final方法。
- CGLIB动态代理是通过字节码底层继承要代理类来实现,如果被代理类被final关键字所修饰,那么代理会失败么?
代理不会失败,只会直接调用目标类的final方法。如update方法是final的,代理类不会做其他的处理,会直接调用UserDAO的update方法。
- CGLIB到底比JDk Proxy快在哪里?
测试环境JDK1.8,平台macOS Catalina。
一般意义上认为CGLIB在创建动态代理类比JDK Proxy慢,但是在方法调用上CGLIB比JDK Proxy快。
但是通过个人的测试,CGLIB在创建动态代理类确实比JDK Proxy慢一点,基本上CGLIB创建一个动态代理类实例需要30ms,JDK Proxy基本上也就10ms。
方法调用上100w次JDK proxy用时33233ms,CGLIB用时34259ms。两者差距并不是很明显。
- 为什么CGLIB要使用fastclass机制?
JDK之前的版本对于反射调用优化不是特别好,反射方法调用很慢,所以为了避免反射带来的性能消耗,采用fastclass机制,fastclass其实就是把需要需要调用的目标方法进行封装,获取到每个方法的索引值,调用时,通过调用方法获取索引值,直接调用到封装的目标方法。这种调用和直接调用性能差别不大。
- 本文链接: http://blog.programer.group/java/2019-10-04-cglib/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!