Spring AOP (三) CGLIB 动态代理
在深入理解之前,我们先来看一个简单的例子。
首先,导入 CGLIB 的 Maven 依赖。
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.11</version>
</dependency>
Spring AOP
的 org.springframework.cglib
包中包含了 CGLIB 的相关代码(和 CGLIB Maven 依赖中代码的一样,版本可能不同),所以也可以选择导入 Spring AOP
的 Maven 依赖。
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
这里选择导入 CGLIB 依赖(因为 Spring 框架中的 cglib 不能下载源码)。
然后,定义一个 Service
类,其有两个方法并且其中一个方法用 final
来修饰。
public class Service {
/**
* final 方法不能被子类覆盖
*/
public final void finalMethod() {
System.out.println("Service.finalMethod 执行了");
}
public void publicMethod() {
System.out.println("Service.publicMethod 执行了");
}
}
接下来,定义一个 MethodInterceptor
的实现类。
public class CglibDynamicProxy implements MethodInterceptor {
/**
* 目标对象(也被称为被代理对象)
*/
private Object target;
public CglibDynamicProxy(Object target) {
this.target = target;
}
/**
*
* @param obj CGLIB 生成的代理对象
* @param method 被代理对象方法
* @param args 方法入参
* @param proxy 方法代理
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CglibDynamicProxy intercept 方法执行前-------------------------------");
System.out.println("obj = " + obj.getClass());
System.out.println("method = " + method);
System.out.println("proxy = " + proxy);
Object object = proxy.invoke(target, args);
System.out.println("CglibDynamicProxy intercept 方法执行后-------------------------------");
return object;
}
/**
* 获取被代理接口实例对象
*
* 通过 enhancer.create 可以获得一个代理对象,它继承了 target.getClass() 类
*
* @param <T>
* @return
*/
public <T> T getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return (T) enhancer.create();
}
}
通过 Client
组合上述代码,进行测试。
public class Client {
public static void main(String[] args) {
// 1. 构造目标对象
Service target = new Service();
// 2. 根据目标对象生成代理对象
CglibDynamicProxy proxy = new CglibDynamicProxy(target);
// 获取 CGLIB 代理类
Service proxyObject = proxy.getProxy();
// 调用代理对象的方法
proxyObject.finalMethod();
proxyObject.publicMethod();
}
}
测试结果如下所示:
测试结果.jpg源码解析
上文中的是通过 enhancer.create
方法调用获取的代理对象,以此为入口深入探究一下 CGLIB 动态代理的实现原理。
// 生成代理类名称是用到了 SOURCE 字段
private static final Source SOURCE = new Source(Enhancer.class.getName());
public Enhancer() {
super(SOURCE);
}
public Object create() {
return createHelper();
}
private Object createHelper() {
Object key = ...;
Object result = super.create(key);
return result;
}
private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>();
protected Object create(Object key) {
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
// 也就是 ClassLoaderData 的 get 方法
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
}
接下来我们关注 ClassLoaderData
的 get
方法的逻辑。
public Object get(AbstractClassGenerator gen, boolean useCache) {
if (!useCache) {
return gen.generate(ClassLoaderData.this);
} else {
Object cachedValue = generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}
AbstractClassGenerator
类中的 generate
方法逻辑如下所示:
// 代理类字节码的默认生成策略
private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
// 代理类的默认命名策略
private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE;
protected Class generate(ClassLoaderData data) {
Class gen;
try {
ClassLoader classLoader = data.getClassLoader();
if (classLoader == null) {
throw new IllegalStateException("ClassLoader is null while trying to define class " +
getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +
"Please file an issue at cglib's issue tracker.");
}
synchronized (classLoader) {
// 1. 生成代理类的类名
String name = generateClassName(data.getUniqueNamePredicate());
data.reserveName(name);
this.setClassName(name);
}
// 2. 生成代理类字节码的二进制数组
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
synchronized (classLoader) { // just in case
if (protectionDomain == null) {
gen = ReflectUtils.defineClass(className, b, classLoader);
} else {
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
}
}
return gen;
}
private String generateClassName(Predicate nameTestPredicate) {
return namingPolicy.getClassName(namePrefix, source.name, key, nameTestPredicate);
}
CGLIB 通过 DefaultNamingPolicy
类中的 getClassName
方法获取到代理类的名称,逻辑如下所示。
/**
* 为代理类选择一个类名
* @param prefix 被代理类的全路径名称,比如这里的 com.wilimm.proxy.cglib.Service
* @param source 生成代理类的全限定名(如 "net.sf.cglib.Enhance", "net.sf.cglib.reflect.FastClass")
* @param key 表示参数状态的关键对象; 要使缓存正常工作,相等的键应该生成相同的生成类名。默认策略将 key.hashCode() 合并到类名中。
* @param names 如果给定的类名已经在同一个 ClassLoader 中使用,则返回 true 的谓词。
* @return 全限定的代理类类名
*/
public String getClassName(String prefix, String source, Object key, Predicate names) {
String base =
prefix + "$$" +
source.substring(source.lastIndexOf('.') + 1) +
getTag() + "$$" +
Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode());
String attempt = base;
int index = 2;
// 检测生成的代理类名是否已经生成,如果已经生成,则需要添加后缀
while (names.evaluate(attempt))
attempt = base + "_" + index++;
return attempt;
}
protected String getTag() {
return "ByCGLIB";
}
我们简单看一下 DefaultGeneratorStrategy
类中的 generate
方法,这是真正生成代理类的地方。
public byte[] generate(ClassGenerator cg) throws Exception {
DebuggingClassWriter cw = getClassVisitor();
// 生成代理类的地方,代码逻辑过于复杂,暂时忽略
transform(cg).generateClass(cw);
return transform(cw.toByteArray());
}
最后,我们重点关注下 DebuggingClassWriter
类中的 toByteArray
方法,在这个方法中我们可以看到如何保存 CGLIB 代理类 Class 到文件中。
public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";
private static String debugLocation;
static {
debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY);
if (debugLocation != null) {
System.err.println("CGLIB debugging enabled, writing to '" + debugLocation + "'");
}
}
public byte[] toByteArray() {
return (byte[]) java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
byte[] b = ((ClassWriter) DebuggingClassWriter.super.cv).toByteArray();
// 如果 DebuggingClassWriter.DEBUG_LOCATION_PROPERTY 系统属性被设置,则输出代理类到指定目录
if (debugLocation != null) {
String dirs = className.replace('.', File.separatorChar);
try {
new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs();
File file = new File(new File(debugLocation), dirs + ".class");
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
try {
out.write(b);
} finally {
out.close();
}
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}
return b;
}
});
}
}
CGLIB 生成的代理类剖析
由上文可知,把 DebuggingClassWriter.DEBUG_LOCATION_PROPERTY
(也就是 cglib.debugLocation
)系统属性设置为当前项目的根目录,即可保存 CGLIB 生成的代理类到当前项目根目录下。
// 获取当前项目的根目录
String userDir = System.getProperty("user.dir");
//System.setProperty("cglib.debugLocation", userDir);
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, userDir);
运行前文中 Client
的 main
方法,即可在当前项目根目录下看到 CGLIB 生成的代理类 Class 文件,如下图所示。
Service$$EnhancerByCGLIB$$bdabbd96
就是 CGLIB 生成的代理类,它继承了 Service
类。
我们来简单分析下 Service$$EnhancerByCGLIB$$bdabbd96
中的代码结构。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.wilimm.proxy.cglib;
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 Service$$EnhancerByCGLIB$$bdabbd96 extends Service implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0; // 拦截器
private static final Method CGLIB$publicMethod$0$Method; // 被代理方法
private static final MethodProxy CGLIB$publicMethod$0$Proxy; // 代理方法
static void CGLIB$STATICHOOK1() {
// 代理类
Class var0 = Class.forName("com.wilimm.proxy.cglib.Service$$EnhancerByCGLIB$$bdabbd96");
// 被代理类
Class var1;
CGLIB$publicMethod$0$Method = ReflectUtils.findMethods(new String[]{"publicMethod", "()V"}, (var1 = Class.forName("com.wilimm.proxy.cglib.Service")).getDeclaredMethods())[0];
CGLIB$publicMethod$0$Proxy = MethodProxy.create(var1, var0, "()V", "publicMethod", "CGLIB$publicMethod$0");
}
// 代理方法(methodProxy.invokeSuper会调用)
final void CGLIB$publicMethod$0() {
super.publicMethod();
}
// 被代理方法(methodProxy.invoke 会调用,这就是为什么在拦截器中调用 methodProxy.invoke 会死循环,一直在调用拦截器)
public final void publicMethod() {
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$publicMethod$0$Method, CGLIB$emptyArgs, CGLIB$publicMethod$0$Proxy);
} else {
super.publicMethod();
}
}
static {
CGLIB$STATICHOOK1();
}
}
MethodProxy
类非常关键,我们分析一下它的内部逻辑。
public class MethodProxy {
private Signature sig1;
private Signature sig2;
private CreateInfo createInfo;
private final Object initLock = new Object();
private volatile FastClassInfo fastClassInfo;
/**
*
* @param c1 被代理对象 Class
* @param c2 CGLIB 代理对象 Class
* @param desc 入参类型
* @param name1 有拦截器逻辑的方法名 publicMethod
* @param name2 直接调用父类方法的方法名 CGLIB$publicMethod$0
* @return
*/
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
private void init() {
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
// 1. 生成被代理类 FastClass,在这里是 Service$$FastClassByCGLIB$$fdf36b96
fci.f1 = helper(ci, ci.c1);
// 2. 生成代理类 FastClass,在这里是 Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2
fci.f2 = helper(ci, ci.c2);
// 3. 得到被代理类 FastClass 中的 publicMethod 方法签名
fci.i1 = fci.f1.getIndex(sig1);
// 4. 得到代理类 FastClass 中的 CGLIB$publicMethod$0 方法签名
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
private static class FastClassInfo {
FastClass f1; //被代理类 FastClass,在这里是 Service$$FastClassByCGLIB$$fdf36b96
FastClass f2; //代理类 FastClass,在这里是 Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2
int i1; // 被代理类 FastClass 中的 publicMethod方法签名(index)
int i2; // 代理类 FastClass 中的 CGLIB$publicMethod$0 的方法签名
}
/**
* 在相同类型的不同对象上调用原始方法。如果传入的是代理对象,则调用的是 CGLIB 代理类重写的方法
* @param obj 方法调用的对象,如果使用 MethodInterceptor 的 intercept 方法的第一个参数,将导致递归
* @param args 参数列表
*/
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
// 调用 Service$$FastClassByCGLIB$$fdf36b96 类中的 publicMethod 方法
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (IllegalArgumentException e) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw e;
}
}
/**
* 在代理对象上调用父类的原始方法
* @param obj CGLIB 生成的代理对象
* @param args 参数列表
*/
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
// 调用 Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2 类中的 CGLIB$publicMethod$0 方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
}
从上面的代码分析中,我们可以得知 MethodProxy
类有两个主要的方法。
-
invoke
:调用代理类中被调用的方法,即Service$$EnhancerByCGLIB$$bdabbd96
中的publicMethod
方法。根据 Java 多态特性可知:- 如果果传入代理对象,即调用代理类的增强方法,
- 如果传入目标对象,调用未增强的目标类的目标方法。
-
invokeSuper
: 直接在代理类中调用父类的方法,即Service$$EnhancerByCGLIB$$bdabbd96
中的CGLIB$publicMethod$0
方法。
FastClass 机制
还记得之前两个 FastClass 文件吗?
-
Service$$FastClassByCGLIB$$fdf36b96
是被代理类的 FastClass。 -
Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2
是代理类的 FastClass。
CGLIB 动态代理执行代理方法效率之所以比JDK 动态代理高,是因为 CGLIB 采用了 FastClass 机制。
下面我们先来看一下被代理类的 FastClass Service$$FastClassByCGLIB$$fdf36b96
。
package com.wilimm.proxy.cglib;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
public class Service$$FastClassByCGLIB$$fdf36b96 extends FastClass {
public Service$$FastClassByCGLIB$$fdf36b96(Class var1) {
super(var1);
}
// 根据方法签名,获取到 FastClass 中的方法 index 值
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -560613858:
if (var10000.equals("finalMethod()V")) {
return 1;
}
break;
case -433379701:
if (var10000.equals("publicMethod()V")) {
return 0;
}
break;
}
return -1;
}
/**
* 调用 FastClass 中的方法
* @param var1 方法 index 值
* @param var2 方法所在的类的对象
* @param var3 参数列表
*/
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
// 强制转换 Object 为 Service 类型,从而可以直接调用 Service 中定义的方法
Service var10000 = (Service)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.publicMethod();
return null;
case 1:
var10000.finalMethod();
return null;
case 2:
return new Boolean(var10000.equals(var3[0]));
case 3:
return var10000.toString();
case 4:
return new Integer(var10000.hashCode());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
}
然后我们再来看一下代理类的 FastClass Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2
。
package com.wilimm.proxy.cglib;
import com.wilimm.proxy.cglib.Service..EnhancerByCGLIB..bdabbd96;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.reflect.FastClass;
public class Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2 extends FastClass {
public Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2(Class var1) {
super(var1);
}
// 根据方法签名,获取到 FastClass 中的方法 index 值
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -560613858:
if (var10000.equals("finalMethod()V")) {
return 21;
}
break;
case -433379701:
if (var10000.equals("publicMethod()V")) {
return 9;
}
break;
case 920107676:
if (var10000.equals("CGLIB$publicMethod$0()V")) {
return 16;
}
break;
return -1;
}
/**
* 调用 FastClass 中的方法
* @param var1 方法 index 值
* @param var2 方法所在的类的对象
* @param var3 参数列表
*/
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
// 强制转换 Object 为 bdabbd96 代理类类型,从而可以直接调用 代理类中定义的方法
bdabbd96 var10000 = (bdabbd96)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:
return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 5:
return var10000.newInstance((Callback[])var3[0]);
case 6:
return var10000.newInstance((Callback)var3[0]);
case 7:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 8:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 9:
var10000.publicMethod();
return null;
case 10:
bdabbd96.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 11:
bdabbd96.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 12:
return var10000.getCallbacks();
case 13:
return var10000.getCallback(((Number)var3[0]).intValue());
case 14:
return bdabbd96.CGLIB$findMethodProxy((Signature)var3[0]);
case 15:
bdabbd96.CGLIB$STATICHOOK1();
return null;
case 16:
var10000.CGLIB$publicMethod$0();
return null;
case 17:
return var10000.CGLIB$clone$4();
case 18:
return new Integer(var10000.CGLIB$hashCode$3());
case 19:
return var10000.CGLIB$toString$2();
case 20:
return new Boolean(var10000.CGLIB$equals$1(var3[0]));
case 21:
var10000.finalMethod();
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
}
FastClass 的原理简单来说就是:为不需要反射 invoke
调用的原类型生成一个 FastClass 类,然后给原类型的方法分配一个 index
,在生成的 FastClass
中的 invoke
方法中,先直接把 Object
强制转换为原类型,然后根据这个 index
,就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK 动态代理通过反射调用高。
至此,我们基本上了解了 CGLIB 动态代理的实现原理。
CGLIB 和 JDK 动态代理区别
说完了 CGLIB 动态代理和 JDK 动态代理之后,我们总结一下两者的区别:
-
JDK 动态代理基于接口,CGLIB 动态代理基于类。因为 JDK 动态代理生成的代理类需要继承
java.lang.reflect.Proxy
,而 Java 只支持单继承,所以只能基于接口。 -
JDK 动态代理和 CGLIB 动态代理都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLIB 使用 ASM 框架写 Class 字节码。
-
JDK 通过反射机制调用方法,CGLIB 通过 FastClass 机制直接调用方法,所以 CGLIB 执行的效率更高。
(正文完)
扩展阅读
喜欢本文的朋友们,欢迎关注微信公众号【程序员小课堂】,阅读更多精彩内容!
程序员小课堂.jpg