CGLIB动态代理实现原理分析

CGLIB介绍

​ CGLIB是一个功能强大的高性能代码生成库。它被广泛应用于基于代理的AOP框架(如springaop和dynaop)中,以提供方法拦截。Hibernate作为最流行的ORM工具,也使用CGLIB库代理单端关联(除了collection lazy loading,它使用另一种机制)。EasyMock和jMock是流行的Java测试库。它们提供模拟对象来支持测试。两者都使用CGLIB代理没有接口的类。
​ CGLIB库使用ASM(一种轻量级但高性能的字节码操作框架)来转换字节码并生成新的类(如图1.1)。

图1.1

除了CGLIB之外,Groovy和Bean Shell等脚本语言也使用ASM来生成Java字节码。ASM使用类似于SAX analyzer的机制来实现高性能。但是,并不推荐直接使用ASM,因为这需要对JVM有很深的了解,包括类文件格式和指令集。
另外,像Spring AOP和Hibernate这样的框架经常使用CGLIB和JDK动态代理来满足各自的需求。Hibernate使用JDK动态代理为webshere应用服务实现事务管理适配器;Spring AOP默认情况下使用JDK动态代理来代理接口,除非强制使用CGLIB,或者代理没有实现接口的类。

CGLIB API

​ CGLIB库虽然只有少量代码,但是由于缺乏文档,学习难度较大。CGLIB库的包结构如图2.1(本文采用Cglib 3.1):

图2.1

  • net.sf.cglib.beans:Java bean 相关的工具类
  • net.sf.cglib.core:底层字节码操作相关的类;大多数与ASP相关
  • net.sf.cglib.proxy:代理创建和方法拦截相关的类
  • net.sf.cglib.reflect:反射相关的类
  • net.sf.cglib.transform:编译和运行时类文件转换相关的类
  • net.sf.cglib.util:集合排序工具相关类

对于创建动态代理,在大多数情况下,只需要使用代理包的部分API。
如上所述,CGLIB库基于ASM的上层应用程序。CGLIB对于代理不实现接口的类非常有用。本质上,对于需要代理的类,它只是动态地生成一个子类来覆盖非final方法,同时绑定钩子函数来回调自定义拦截器,这样就可以对目标类进行增强。值得一提的是,它比JDK动态代理更快。

net.sf.cglib.proxy.MethodInterceptor是最常用的回调类型,通常用于在基于代理的AOP实现中拦截方法回调。此接口只有一个方法:

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
/**
* General-purpose {@link Enhancer} callback which provides for "around advice".//提供环绕通知的能力
* @author Juozas Baliuka <a href="mailto:[email protected]">[email protected]</a>
* @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
*/
public interface MethodInterceptor
extends Callback
{
/**
* All generated proxied methods call this method instead of the original method.
* The original method may either be invoked by normal reflection using the Method object,
* or by using the MethodProxy (faster).
* @param obj "this", the enhanced object // 代理对象
* @param method intercepted Method // 目标方法
* @param args argument array; primitive types are wrapped // 目标方法的参数
* @param proxy used to invoke super (non-intercepted method); may be called
* as many times as needed
* @throws Throwable any exception may be thrown; if so, super method will not be invoked
* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
* @see MethodProxy
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;

}

如果net.sf.cglib.proxy.MethodInterceptor被设置为方法回调,当调用代理方法时,它调会用MethodInterceptor.intercept方法,然后再调用代理对象的方法,也就是目标方法。MethodInterceptor的intercept方法的第一个参数是代理对象,第二个参数和第三个参数分别是被拦截的方法和方法的参数,最后一个参数是代理方法。如果要调用代理对象的原始方法,可以使用java.lang.reflect方法对象来反射调用,或net.sf.cglib.proxy.MethodProxy对象。我们通常用net.sf.cglib.proxy.MethodProxy.MethodProxy,因为它更快。在intercept方法中,可以在目标方法调用之前或之后注入自定义代码(环绕通知)。

net.sf.cglib.proxy.MethodInterceptor满足大部分场景代理要求,但对于某些特定场景可能不太方便。为了易于使用和高性能,CGLIB提供了其他特殊的回调类型。例如,

  • net.sf.cglib.proxy.FixedValue:在特定情况下,强制特定方法返回固定值,是非常有用和高性能的。
  • net.sf.cglib.proxy.NoOp:它直接传递父类的方法实现。
  • net.sf.cglib.proxy.LazyLoader:这在需要延迟加载代理对象的情况下非常有用。如果加载了代理对象,它将在以后的代理调用中重用。
  • net.sf.cglib.Dispatcher:类似于net.sf.cglib.proxy.LazyLoader,但每次调用代理方法时,都会调用loadObject方法来加载代理对象。
  • net.sf.cglib.proxy.ProxyRefDispatcher:与Dispatcher相同,但其loadObject方法支持传入的代理对象。

我们通常对代理类的所有方法使用相同的回调,但是我们也可以使用net.sf.cglib.proxy.callsFilter对不同的方法使用不同的回调。然而JDK动态代理并没有提供这样的细粒度的控制。也就是说JDK中, java.lang.reflect.InvocationHandler的invoke方法不能对不同方法使用不同回调,只能使用相同的回调。

现在让我们看看如何使用CGLIB创建代理。

用CGLIB 创建代理

​ 使用Cglib可以很方便的创建代理。

创建一个简单的代理

​ 只需要传入一个目标类即可,Cglib就可以为该类创建代理对象。

1
2
3
4
5
6
7
8
9
10
  /**
* @param targetClass 目标类
* @return 代理对象
*/
public Object createProxy(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(NoOp.INSTANCE);
return enhancer.create();
}

此方法的返回值是目标类对象的代理。在上面的例子中,net.sf.cglib.proxy.Enhancer 配置了一个net.sf.cglib.proxy.Callback。如你所见,使用CGLIB创建一个简单的代理很容易。除了new一个net.sf.cglib.proxy.Enhancer对象,也可以net.sf.cglib.proxy.Enhancer中的静态辅助方法直接创建。但是建议使用示例中的方法,因为可以配net.sf.cglib.proxy.Enhancer对象来更精确地控制生成的代理。

值得注意的是,我们将目标类作为代理的父类传入。与JDK动态代理不同,我们不能使用目标对象来创建代理。目标对象只能由CGLIB创建。在本例中,默认的参数化构造函数用于创建目标对象。如果希望CGLIB用参数创建一个实例,那么应该使用net.sf.cglib.proxy.Enhancer.create(Class[],Object[])。方法的第一个参数指定参数类型,第二个参数指定参数值。参数中的基本类型需要包装类型。

创建一个复杂的代理

​ 说复杂其实也不复杂,只是相对与上面简单类型而言,多了一些回调接口来实现不同类型的通知对目标类增强,例如前置通知等。

使用MethodInterceptor

​ 我们可以替换net.sf.cglib.proxy.NoOp,实现net.sf.cglib.proxy.MethodInterceptor接口以获得更强的代理。代理对象的所有方法调用都分配给的intercept方法net.sf.cglib.proxy.MethodInterceptor. intercept方法然后调用底层对象。

​ 假设您要检查是否有权限调用目标对象的某个方法,如果授权失败,可以抛出一个运行时异常AuthorizationException(本例中只是简单打印信息)。接口授权.java具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.wonglay;

import java.lang.reflect.Method;

/**
* @author wonglay
* @description 简单的授权服务
*/
public interface AuthorizationService {

void authorize(Method method);
}

net.sf.cglib.proxy.MethodInterceptor接口的实现如下:

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
package com.wonglay;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* @author wonglay
* @description 一个简单的MethodInterceptor实现,应用AuthorizationService授权服务
* 在调用代理方法前检查权限
*/
public class AuthorizationInterceptor implements MethodInterceptor {

private final AuthorizationService authorizationService;

public AuthorizationInterceptor(AuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if (authorizationService != null) {
// 如果授权失败,可以抛出AuthorizationException
authorizationService.authorize(method);
}
return methodProxy.invokeSuper(obj, args);
}
}

在intercept方法中,首先检查授权,如果授权通过,intercept方法调用目标对象的方法。出于性能原因,我们使用CGLIB net.sf.cglib.proxy.MethodProxy对象而不是常规java.lang.reflect方法反射对象来调用原始方法。

使用CallbackFilter

​ net.sf.cglib.proxy.callsFilter允许在方法级别设置回调。假设有一个PersistenceServiceImpl类,它有两个方法:save和load。save方法需要授权检查,而load方法不需要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.wonglay;

/**
* @author wonglay
* @description PersistenceService接口简单实现
*/
public class PersistenceServiceImpl implements PersistenceService {

@Override
public void save(Long id, String data) {
System.out.println(data + " has been saved successfully");
}

@Override
public String load(Long id) {
return "wonglay.com";
}
}

PersistenceService接口的net.sf.cglib.proxy.CallbackFilter实现

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
package com.wonglay;

import net.sf.cglib.proxy.CallbackFilter;

import java.lang.reflect.Method;

/**
* @author wonglay
* @description
*/
public class PersistenceServiceCallbackFilter implements CallbackFilter {

// save方法在callback数组中的下标
public static final int SAVE = 0;

// save方法在callback数组中的下标
public static final int LOAD = 1;

/**
* 指定哪个callback被调用当这个方法被调用的时候,callback
* 就是一个MethodInterceptor
* @param method 正在被调用的方法
* @return 这个方法在callback数组中的下标
*/
@Override
public int accept(Method method) {
String methodName = method.getName();
if ("save".equals(methodName)) {
return SAVE;
}
return LOAD;
}
}

accept方法将代理方法映射到MethodInterceptor,也即callback。这个方法返回callback对象数组的下标。下面是为PersistenceServiceImpl类创建代理对象的过程:

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
package com.wonglay;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;

/**
* @author wonglay
* @description
*/
public class Main {

public static void main(String[] args) {

Enhancer enhancer = new Enhancer();
// 设置需要代理的类
enhancer.setSuperclass(PersistenceServiceImpl.class);
// 设置PersistenceServiceImpl需要增强(检查权限)的方法过滤器
CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();
enhancer.setCallbackFilter(callbackFilter);
// AuthorizationService 的简单实现
AuthorizationService authorizationService = (method) -> {
String methodName = method.getName();
System.out.println("[" + methodName + "] method authenticating...");
};

Callback saveCallback = new AuthorizationInterceptor(authorizationService);
Callback loadCallback = NoOp.INSTANCE;
// 设置Callback
Callback[] callbacks = new Callback[]{saveCallback, loadCallback};
enhancer.setCallbacks(callbacks);
// 创建代理对象
PersistenceServiceImpl persistenceService = (PersistenceServiceImpl) enhancer.create();
System.out.println(persistenceService.load(1L));
persistenceService.save(2L, "wonglay.com");
}
}

打印结果如图3.2.2,load方法调用没有打印权限检查信息,而save方法打印了权限检查信息,原因是load方法使用了NoOp.INSTANCE callback,它只是一个线程安全的空实现,而save方法使用了AuthorizationInterceptor callback对目标类进行增加,这里的通知(Advice)类型为前置通知。

图3.2.2

在本例中,授权拦截器应用于save方法,NoOp.INSTANCE 应用于load方法。你可以通net.sf.cglib.proxy.Enhancer.setInterfaces(Class [])指定代理需要实现的接口,但这不是必需的。
对于net.sf.cglib.proxy.Enhancer,除了设置callback对象的数组外,还可以使用net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class [])设置callback对象的数组。如果在代理创建期间没有实际的callback对象,则此方法非常有用。与callback对象一样,还需要使用net.sf.cglib.proxy.callsFilter为每个拦截方法指定callback对象数组的下标。

底层实现原理探究

​ 当调用Enhancer的create方法创建代理对象时底层回调用下面这个方法:

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
protected Object create(Object key) {
Class gen = null;
synchronized (source) {
ClassLoader loader = getClassLoader();
Map cache2 = null;
cache2 = (Map)source.cache.get(loader);
if (cache2 == null) {
cache2 = new HashMap();
cache2.put(NAME_KEY, new HashSet());
source.cache.put(loader, cache2);
} else if (useCache) {
Reference ref = (Reference)cache2.get(key);
gen = (Class) (( ref == null ) ? null : ref.get());
}
if (gen == null) {
// 先从当前线程获取
Object save = CURRENT.get();
CURRENT.set(this);
try {
this.key = key;
if (attemptLoad) {
try {
gen = loader.loadClass(getClassName());
} catch (ClassNotFoundException e) {
// ignore
}
}
if (gen == null) {
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
getClassNameCache(loader).add(className);
// 生成Class对象
gen = ReflectUtils.defineClass(className, b, loader);
}
if (useCache) {
cache2.put(key, new WeakReference(gen));
}
// 利用反射创建对象
return firstInstance(gen);
} finally {
CURRENT.set(save);
}
}
}
return firstInstance(gen);
}

我们可以通过给JVM加一个Debug属性,使生成的代理对象输出到磁盘:

1
2
3
4
5
6
7
8
public static void main(String[] args) {
// 将CGLIB用字节码框架ASM生成的Class文件输出的到磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java");

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersistenceServiceImpl.class);
....
}

生成的Class文件用IDE反编译得到如下代理类,此对象继承了目标对象,并且实现了Factory接口,该接口可用于创建此代理类型相同的实例,

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

package com.wonglay;

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;
import net.sf.cglib.proxy.NoOp;

public class PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a extends PersistenceServiceImpl implements Factory {
// callbacks函数是否要和当前线程绑定
private boolean CGLIB$BOUND;
// 用于存放需要和当前线程绑定的callbacks
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
// 本例中对应AuthorizationInterceptor
private MethodInterceptor CGLIB$CALLBACK_0;
// 本例中对应NoOp.INSTANCE
private NoOp CGLIB$CALLBACK_1;
// save的Method对象
private static final Method CGLIB$save$1$Method;
// 为save方法创建的代理对象
private static final MethodProxy CGLIB$save$1$Proxy;
private static final Object[] CGLIB$emptyArgs;

/**
* CGLIB$save$1$Method 和 CGLIB$save$1$Proxy 对象就是MethodInterceptor接口的入参
* 用MethodProxy 比用 Method反射调用方法更快
*
*/
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.wonglay.PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a");
Class var1;
// 通过方法名称和方法描述符获取到save的Method对象
CGLIB$save$1$Method = ReflectUtils.findMethods(new String[]{"save", "(Ljava/lang/Long;Ljava/lang/String;)V"}, (var1 = Class.forName("com.wonglay.PersistenceServiceImpl")).getDeclaredMethods())[0];
// 为方法创建一个代理方法
CGLIB$save$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Long;Ljava/lang/String;)V", "save", "CGLIB$save$1");
}

final void CGLIB$save$1(Long var1, String var2) {
super.save(var1, var2);
}

/**
* 此方法重载了PersistenceServiceImpl的save方法,由于load方法的callback函数为NoOp.INSTANCE
* ,是一个空实现,所以未重载load方法,所以会直接调用PersistenceServiceImpl的load方法
*/
public final void save(Long var1, String var2) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}

if (var10000 != null) {
// 此时调用MethodInterceptor的interceptor方法,在此interceptor方法进行通知
// CGLIB$save$1$Method,CGLIB$save$1$Proxy参数在静态块中已经初始化了
var10000.intercept(this, CGLIB$save$1$Method, new Object[]{var1, var2}, CGLIB$save$1$Proxy);
} else {
super.save(var1, var2);
}
}

public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -279689439:
if (var10000.equals("save(Ljava/lang/Long;Ljava/lang/String;)V")) {
return CGLIB$save$1$Proxy;
}
}
return null;
}

public PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a() {
CGLIB$BIND_CALLBACKS(this);
}

public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}

public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}

// 将callbacks绑定到当前线下
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a var1 = (PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a)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;
}
}

Callback[] var10001 = (Callback[])var10000;
var1.CGLIB$CALLBACK_1 = (NoOp)((Callback[])var10000)[1];
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)var10001[0];
}

}
// 实现了Factory接口的方法,用于创建此类的实例
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a var10000 = new PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}

public Object newInstance(Callback var1) {
throw new IllegalStateException("More than one callback object required");
}
// 实现了Factory接口的方法,用于创建此类的实例
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a var10000 = new PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
// 实现了Factory接口的方法
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
Object var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
case 1:
var10000 = this.CGLIB$CALLBACK_1;
break;
default:
var10000 = null;
}

return (Callback)var10000;
}
// 实现了Factory接口的方法
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
break;
case 1:
this.CGLIB$CALLBACK_1 = (NoOp)var2;
}

}
// 实现了Factory接口的方法
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0, this.CGLIB$CALLBACK_1};
}
// 实现了Factory接口的方法
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
this.CGLIB$CALLBACK_1 = (NoOp)var1[1];
}
// JVM加载这个类时会进行初始化
static {
CGLIB$STATICHOOK1();
}
}

生成的代理对象继承了目标对象,并重写了目标对象的方法以此来达到增强目标对象的目的。实现net.sf.cglib.proxy.Factory的目的是提供一些创建代理对象实例的工厂方法,这些方法会比反射创建对象快。另外,要拦截在对象构造期间调用的方法,必须使用这些方法而不是反射。

总结

​ CGLIB是一个功能强大的高性能代码生成库。作为对JDK动态代理的补充,它为不实现接口的类提供代理解决方案。在底部,它使用ASM字节码操作框架。实际上,CGLIB通过生成子类来重写非final方法来代理。它比使用Java反射的JDK动态代理方法更快。CGLIB不能代理final类或final方法。一般来说,可以使用JDK动态代理方法来创建代理。对于没有接口或性能因素的情况,CGLIB是一个很好的选择。

Author: wonglay
Link: https://wonglay.com/2020/12/01/cglib-dynamic-proxy-implementation-principle/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.