本文承接自动态代理浅析这篇文章,对代理没有什么概念的同学建议先读下这篇文章。

本文打算从这几个方面来理解CGLIB怎样生成动态代理类的:

  1. 怎样使用CGLIB?
  2. CGLIB怎样生成/缓存动态代理类实例的?
  3. CGLIB生成的动态代理类怎样调用的,过程是怎样的,这样有什么好处?
  4. 常见问题FAQ

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
20
Enhancer 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
16
public 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
11
public 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-cache

上图是CGLIB缓存动态代理类的Class对象的结构。

CGLIB第一次生成动态代理类Class对象的方法调用流程:

cglib-method

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 FastClassUserDao$$FastClassByCGLIB$$890e5f18 extends FastClass

UserDao$$EnhancerByCGLIB$$c00e2e9b类的静态初始化块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 当前线程的缓存
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
// 空参数对象
CGLIB$emptyArgs = new Object[0];
// 动态代理类的Class对象
Class var0 = Class.forName("com.zhongyp.advanced.proxy.cglib.UserDao$$EnhancerByCGLIB$$c00e2e9b");
Class var1;
// 只获取update和select的方法对象
Method[] var10000 = ReflectUtils.findMethods(new String[]{"update", "()V", "select", "()V"}, (var1 = Class.forName("com.zhongyp.advanced.proxy.cglib.UserDao")).getDeclaredMethods());
CGLIB$update$0$Method = var10000[0];
// 记住这里,后续我们会细聊这一块MethodProxy.create
CGLIB$update$0$Proxy = MethodProxy.create(var1, var0, "()V", "update", "CGLIB$update$0");
CGLIB$select$1$Method = var10000[1];
// 记住这里,后续我们会细聊这一块MethodProxy.create
CGLIB$select$1$Proxy = MethodProxy.create(var1, var0, "()V", "select", "CGLIB$select$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
15
private 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
2
3
4
5
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// methodProxy.invokeSuper
Object result = methodProxy.invokeSuper(object, objects);
return result;
}

methodProxy,还记得动态代理类的static代码块么,在那时就已经初始化好了methodProxy:

1
2
3
4
5
6
7
8
9
public 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
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
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
// 这里调用的实际是FastClass代理类中的invoke方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
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();
// UserDAO和UserDAO的代理类分别有自己的FastClassInfo,分别使用个字的ClassInfo生成FastClass代理类
// 生成UserDAO的FastClass代理类
fci.f1 = helper(ci, ci.c1);
// 生成UserDAO代理类的FastClass代理类
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;
}
}
}

}

根据索引调用方法,fci.i2的值为12,调用索引12的方法。
UserDao$$EnhancerByCGLIB$$c00e2e9b$$FastClassByCGLIB$$ff71decc动态代理类:

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
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
c00e2e9b var10000 = (c00e2e9b)var2;
int var10001 = var1;

try {
switch(var10001) {
case 0:
return new Boolean(var10000.equals(var3[0]));
case 1:
return var10000.toString();
case 2:
return new Integer(var10000.hashCode());
case 3:
return var10000.clone();
case 4:
var10000.update();
return null;
case 5:
return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 6:
return var10000.newInstance((Callback)var3[0]);
case 7:
return var10000.newInstance((Callback[])var3[0]);
case 8:
var10000.select();
return null;
case 9:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 10:
return c00e2e9b.CGLIB$findMethodProxy((Signature)var3[0]);
case 11:
c00e2e9b.CGLIB$STATICHOOK1();
return null;
case 12:
var10000.CGLIB$update$0();
return null;
case 13:
var10000.CGLIB$select$1();
return null;
case 14:
return new Boolean(var10000.CGLIB$equals$2(var3[0]));
case 15:
return var10000.CGLIB$toString$3();
case 16:
return new Integer(var10000.CGLIB$hashCode$4());
case 17:
return var10000.CGLIB$clone$5();
case 18:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 19:
c00e2e9b.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 20:
c00e2e9b.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 21:
return var10000.getCallback(((Number)var3[0]).intValue());
case 22:
return var10000.getCallbacks();
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}

throw new IllegalArgumentException("Cannot find matching method/constructor");
}

访问流程图:

cglib-invoke

总结

无论是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
17
public 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
76
public 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

  1. CGLIB针对final方法怎么处理的呢?

不处理,因为继承了目标类,所以动态代理类实例可以直接调用目标类的final方法。

  1. CGLIB动态代理是通过字节码底层继承要代理类来实现,如果被代理类被final关键字所修饰,那么代理会失败么?

代理不会失败,只会直接调用目标类的final方法。如update方法是final的,代理类不会做其他的处理,会直接调用UserDAO的update方法。

  1. 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。两者差距并不是很明显。

  1. 为什么CGLIB要使用fastclass机制?

JDK之前的版本对于反射调用优化不是特别好,反射方法调用很慢,所以为了避免反射带来的性能消耗,采用fastclass机制,fastclass其实就是把需要需要调用的目标方法进行封装,获取到每个方法的索引值,调用时,通过调用方法获取索引值,直接调用到封装的目标方法。这种调用和直接调用性能差别不大。