Spring AOP的两种实现:JDK/CGLib动态代理

前言

AOP 代理主要分为静态代理和动态代理,静态代理的代表为 AspectJ,而动态代理则以 Spring AOP 为代表。静态代理是编译期实现,动态代理是运行期实现,静态代理拥有相对更好的性能。

静态代理在编译阶段生成 AOP 代理类,生成的字节码就织入了增强后的 AOP 对象。动态代理则不会修改字节码,而是在内存中临时生成一个 AOP 对象,这个 AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

Spring AOP 中的动态代理

Spring AOP 中存在两种代理实现机制,分别是 JDK 动态代理和 CGLib 动态代理。

Spring 中虽然使用了与 AspectJ 一样的注解,没有使用 AspectJ 的编译器 ,采用的仍是 Spring AOP 动态代理,而是 AspectJ 静态代理。

Spring 在选择用 JDK 还是 CGLib 的依据:

  1. 当 Bean 实现接口时,Spring 就会用 JDK 的动态代理。
  2. 当 Bean 没有实现接口时则使用 CGlib实现。

注:从 Spring Boot 2.0 开始,默认的 AOP 策略已经更换为 CGLIB,即 proxy-target-class = true。

JDK 动态代理

JDK 动态代理使用步骤

  1. 定义 InvocationHandler 类并实现 InvocationHandler 接口。

  2. 通过 Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) 方法生成动态代理类实例。

  3. 通过生成的动态代理类实例调用目标方法,目标方法的调用会被转发给 InvocationHandler 类中的 invoke() 方法处理。

JDK 动态代理源码分析

JDK 动态代理通过反射创建 Class 并动态生成类字节码加载到 JVM 中,并且要求被代理的类必须实现一个接口,核心是 InvocationHandler 接口和 Proxy 类。

1
2
3
4
5
6
7
public interface InvocationHandler {
// InvocationHandler is the interface implemented by the invocation handler of a proxy instance
// InvocationHandler接口由proxy实例的invocation handler实现
// 当通过proxy实例调用一个方法时候,这个方法的调用就会被转发给实现了InvocationHandler接口的invocation handler中的invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

Proxy 类则是用来创建一个代理对象的类,先来看看生成代理对象的 newProxyInstance() 方法,这个方法接收接受以下三个参数:

  • ClassLoader loader:定义由哪个 Classloader 对象负责加载生成的代理对象。
  • Class<?>[] interfacess:接口数组,表示提供给代理对象的一组接口,代理类可以调用接口数组中实现的所有方法。
  • InvocationHandler h:表面当代理对象调用方法的时候会关联到哪一个 InvocationHandler 对象上,并最终由其调用。

通过 Proxy.newProxyInstance() 方法创建的代理对象是在 JVM 运行时动态生成的一个对象。

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
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 检查InvocationHandler h不为空,否则抛异常
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
// 检查权限
// Check permissions required to create a Proxy class.
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

/*
* 查找或生成给定的代理类
* 实现动态代理的核心方法
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);

/*
* 通过invocation handler调用构造方法
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 获取代理对象的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 通过构造器生成代理类实例返回
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
// 省略
...
}
}
1
2
3
4
5
6
7
8
9
10
11
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 当传入的接口数组长度大于65535将会抛出异常.....65535个接口....
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

// 从proxyClassCache中取缓存,proxyClassCache是一个代理类的缓存变量
// 如果proxyClassCache中存在指定的代理类则直接返回,如果不存在会通过ProxyClassFactory内部工厂类创建代理对象
return proxyClassCache.get(loader, interfaces);
}

在 ProxyClassFactory 工厂类内,会通过 ProxyGenerator 类的 generateProxyClass() 方法生成代理类的字节码。

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
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
// 实际是调用generateClassFile()方法生成代理类的byte数组
final byte[] var4 = var3.generateClassFile();
// 保存代理类的字节码文件
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
// 保存至本地磁盘
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}

return var4;
}

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
private byte[] generateClassFile() {
// 将接口中的方法和Object中的方法添加到ProxyMethod中
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;

int var3;
Class var4;
// 遍历接口加入到ProxyMethod中
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;

for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}

Iterator var11 = this.proxyMethods.values().iterator();

List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}

Iterator var15;
try {
// 添加构造函数
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();

while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
// 添加方法及属性
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}

this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
// 代理类中的方法数量超过65535则抛出异常
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
// 代理类中的属性数量超过65535则抛出异常
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;

for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}

this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
// 写入操作,不细看了
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();

while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}

CGLib 动态代理

CGLib 的 GitHub Wiki 上是这么介绍的:

“ cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.”

CGLib 通过继承指定类来生成子类,并覆盖其中的方法来实现动态代理。

CGLib 动态代理使用步骤

  1. 代理类需要实现 MethodInterceptor 接口,MethodInterceptor 接口只有一个方法 intercept(),类似于 JDK Proxy 里面的 invoke() 方法,用来描述织入目标方法的横切逻辑。
  2. 实例化一个 Enhancer 类 ,通过 Enhancer 的 setSuperclass()、setCallback()方法设置继承的父类对象与拦截被代理类方法时的逻辑,然后调用 create() 方法来创建子类代理类实例。
  3. 通过代理类实例调用目标方法。

CGLib 动态代理源码解析

MethodInterceptor 接口中的 intercept() 方法有以下几个参数:

  • Object obj:被代理的原对象。
  • OMethod methodO:被调用的当前方法。
  • Object[] argsO:该方法的参数集合。
  • MethodProxy proxyO:被调用方法的代理,这里使用了 FastClass 机制非反射执行方法,效率比反射执行方法更高。
1
2
3
4
5
6
public interface MethodInterceptor extends Callback {
// 所有的代理方法都会调用该方法而不是原始方法
// 实现了该接口的代理类会被Enhancer回调
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
}

MethodProxy 中有一个内部静态类 FastClassInfo,其中 f1 为被代理对象,f2 为代理类对象,i1 和 i2 分别是代理类中的该方法的两个索引。FastClass 机制并不是通过反射找到指定的方法,而是对每一个方法,根据方法签名的 HashCode 来决定调用哪个方法,这样调用的时候就可以通过索引直接定位方法,相比反射调用效率更高。

1
2
3
4
5
6
7
private static class FastClassInfo
{
FastClass f1;
FastClass f2;
int i1;
int i2;
}

接着来看看 enhancer.setSuperclass() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void setSuperclass(Class superclass) {
// 如果传入的为接口
if (superclass != null && superclass.isInterface()) {
// 作为Class数组保存在superclass中
setInterfaces(new Class[]{ superclass });
// 如果传入的为为Object
} else if (superclass != null && superclass.equals(Object.class)) {
// affects choice of ClassLoader
// 将superclass赋为null
this.superclass = null;
} else {
// 否则将传入的父类直接保存至superclass
this.superclass = superclass;
}
}

enhancer.setCallback() 方法,需要传入一个 CglibProxy 的实例作为回调对象,并且把它放入到一个 Callback 类型的数组中:

1
2
3
4
5
6
7
8
9
10
11
12
public void setCallback(final Callback callback) {
setCallbacks(new Callback[]{ callback });
}

public void setCallbacks(Callback[] callbacks) {
// 判断Callback数组是否不为空
if (callbacks != null && callbacks.length == 0) {
throw new IllegalArgumentException("Array cannot be empty");
}
// 存入本地字段callbacks
this.callbacks = callbacks;
}

最后一步,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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}

private Object createHelper() {
// 提前做一些校验
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
// 创建代理类实例
Object result = super.create(key);
return result;
}

private void preValidate() {
// 校验callbackTypes
if (callbackTypes == null) {
callbackTypes = CallbackInfo.determineTypes(callbacks, false);
validateCallbackTypes = true;
}
// 检查是否设置过滤器,如果设置了多个回调方法就需要设置过滤器
if (filter == null) {
if (callbackTypes.length > 1) {
throw new IllegalStateException("Multiple callback types possible but no filter specified");
}
filter = ALL_ZERO;
}
}

// 可以看出,这里也用了缓存
protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}

JDK 动态代理与 CGLib 动态代理的区别

  • JDK 只能针对实现了接口的类生成代理,CGLib 代理是通过继承目标类实现代理,不需要实现接口,通过对目标类生成一个子类并覆盖其中的所有方法完成动态代理。
  • JDK 采用反射机制调用代理方法,CGLib 将代理方法全部存入一个数组中,通过查找索引直接调用代理方法。
  • CGLib 通过继承的方式实现动态代理,需要重写被代理方法,所以不能代理 static,private,final 修饰的方法。

JDK 动态代理对象实质上是目标对象接口的实现类,CGLib 动态代理对象实质上是目标对象的子类。JDK 只能代理接口是因为它通过反射获得接口中的所有方法,而 CGLib 则获取了目标类中的所有方法和接口中的所有方法。

  • 本文作者: Marticles
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!