文章目录
1 概述
上一篇文章已经介绍过JDK动态代理,相比于JDK动态代理,CGLIB动态代理的实现就复杂很多,本文就一起来探索CGLIB动态代理的实现吧!上一篇文章链接地址 =》 JDK动态代理底层源码分析
CGLIB(Code Generation Library)是一个基于字节码生成的动态代理库,它可以在运行时生成代理类来实现动态代理。相对于基于接口的动态代理(如JDK动态代理),CGLIB可以代理没有实现接口的类。CGLIB的底层原理主要涉及两个核心概念:字节码生成和子类代理
- 字节码生成:CGLIB使用ASM(一个Java字节码操作库)来生成目标类的字节码。它通过读取目标类的字节码,生成一个新的子类,并覆盖其中的方法来实现代理逻辑。CGLIB会生成一个新的代理类,并在运行时加载到内存中
- 子类代理:CGLIB通过生成目标类的子类来实现代理。代理类继承自目标类,并重写了目标类中的方法,将代理逻辑添加到重写的方法中。通过继承关系,CGLIB可以直接调用代理类中的方法,并在需要的时候调用父类的方法
2 步骤
- 创建Enhancer对象:Enhancer是CGLIB库的核心类,用于生成代理类。
- 设置父类:通过设置Enhancer对象的父类来指定目标类。
- 设置回调对象:通过设置Enhancer对象的回调对象,指定代理类中方法的拦截逻辑。回调对象需要实现MethodInterceptor接口(和jdk动态代理实现InvocationHandler接口一样,也是一个函数式接口),并重写intercept()方法,在该方法中添加自定义的代理逻辑。
- 创建代理类:调用Enhancer对象的create()方法来创建代理类。create()方法会根据父类和回调对象生成代理类的字节码,并创建代理对象。
- 使用代理对象:使用生成的代理对象进行方法调用,代理对象会自动调用回调对象的intercept()方法,在该方法中执行代理逻辑。
public static void main(String[] args) {
// 动态代理创建的class文件存储到本地
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "d:\\temp");
// 创建增强器
Enhancer enhancer = new Enhancer();
// 设置enhancer的父类来指定目标类
enhancer.setSuperclass(UserService.class);
// 设置回调处理器
enhancer.setCallback(new UserServiceInterceptor());
// 创建代理对象
UserService proxy = (UserService) enhancer.create();
// 通过代理对象调用目标方法
System.out.println(proxy.get("test"));
}
package com.lin.tesia.proxy.cglib;
import com.lin.tesia.entity.User;
public class UserService {
public int add(User user) {
System.out.println(user);
return 1;
}
public User get(String name){
return new User(name, 3);
}
}
package com.lin.tesia.proxy.cglib;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class UserServiceInterceptor implements MethodInterceptor {
/**
* @param o 代理对象
* @param method 被代理对象原始方法
* @param objects 参数列表
* @param methodProxy 在生成的代理类中会为每个被代理类的方法创建对应的MethodProxy, 后面重点介绍这个
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("1111");
return methodProxy.invokeSuper(o, objects);
}
}
3 cglib核心api
CGLIB框架对外提供的核心类有Enhancer和Callback接口,暴露的API如下:
- Enhancer类:Enhancer是CGLIB库的核心类,负责生成代理类。以下是Enhancer类的关键方法:
- setSuperclass(Class<?> superclass):设置代理类的父类,即目标类。
- setCallback(Callback callback):设置回调对象,指定方法拦截逻辑。
- create():创建代理类并返回代理对象。
- Callback接口:Callback接口是CGLIB中的回调接口,用于定义方法拦截逻辑。CGLIB提供了多个Callback接口的实现,其中最常用的是MethodInterceptor接口。MethodInterceptor接口定义了一个intercept()方法,用于实现代理逻辑:
- MethodInterceptor(方法拦截器):MethodInterceptor接口是CGLIB中最常用的Callback接口实现类,用于定义方法拦截逻辑。它定义了一个intercept()方法,用于在代理对象的方法执行前后插入自定义逻辑
- NoOp(空操作):NoOp是Callback接口的一个实现,它不进行任何操作,即空操作。当不需要对方法进行拦截和修改时,可以使用NoOp来避免不必要的开销和性能损耗
- FixedValue(固定返回值):FixedValue是Callback接口的另一个实现,它可以指定一个固定的返回值。当代理对象的方法被调用时,FixedValue会返回指定的固定值,而不会执行目标方法
- Dispatcher(分派器):Dispatcher是Callback接口的实现,用于根据方法签名将方法调用分派给不同的实现。它可以根据方法名称、参数类型等来选择具体的方法实现
- LazyLoader(延迟加载):LazyLoader是Callback接口的实现,用于实现延迟加载的功能。当代理对象的方法被调用时,LazyLoader会延迟加载目标对象,直到真正需要时才进行加载
- InvocationHandler(调用处理器):InvocationHandler是CGLIB中的一个Callback接口实现类,与Java标准库中的InvocationHandler接口相似。它定义了一个invoke()方法,用于在代理对象的方法执行前后插入自定义逻辑
- LazyLoaderAdapter(延迟加载适配器):LazyLoaderAdapter是Callback接口的实现,用于实现延迟加载的功能。它可以将LazyLoader接口的实现适配为Callback接口,以便在生成代理类时使用
4 cglib源码分析
1)EnhancerKey
查看Enhancer类里有一个EnhancerKey类型的类变量会在类加载的初始化阶段进行赋值,EnhancerKey是一个接口仅有一个newInstance方法,且没有任何实现,很奇怪吧?其实这个EnhancerKey对象是也是通过动态代理生成的,这里先说结论:
EnhancerKey
:EnhancerKey
是一个用于缓存CGLIB生成的代理类的键的类。在CGLIB中,为了提高性能,会对生成的代理类进行缓存,以便在相同的代理请求下直接使用已生成的代理类,而不需要再次生成。EnhancerKey
作为缓存的键,它封装了生成代理类所需的相关信息,如父类、接口列表、回调处理器等。通过比较不同代理请求的EnhancerKey
对象,可以确定是否已经生成过相应的代理类,从而避免重复生成和提高性能。newInstance()
方法:newInstance()
是EnhancerKey
类中的一个方法,用于创建一个新的EnhancerKey
实例。该方法会根据传入的参数(如类加载器、父类、接口列表、回调处理器等)创建一个EnhancerKey
对象,并返回新创建的实例。通常在生成代理类时,会使用newInstance()
方法创建对应的EnhancerKey
对象,然后将其用作缓存的键,以便在需要时进行查找和重用已生成的代理类。
总:
EnhancerKey
类和newInstance()
方法是CGLIB库中用于缓存和区分不同代理类的关键组件。通过使用EnhancerKey
作为缓存的键,可以避免重复生成相同的代理类,提高性能并节省资源。newInstance()
方法用于创建新的EnhancerKey
实例,用于唯一标识代理类的属性和特征
public class Enhancer extends AbstractClassGenerator {
private static final EnhancerKey KEY_FACTORY =
(EnhancerKey) KeyFactory.create(EnhancerKey.class, KeyFactory.HASH_ASM_TYPE, null);
public interface EnhancerKey {
public Object newInstance(String type,
String[] interfaces,
WeakCacheKey<CallbackFilter> filter,
Type[] callbackTypes,
boolean useFactory,
boolean interceptDuringConstruction,
Long serialVersionUID);
}
2)KeyFactory
KeyFactory
是一个用于生成唯一键(Key
)的工厂类,而create()
方法是KeyFactory
类的一个方法,用于创建一个新的键实例。它们的作用如下:
KeyFactory
类:KeyFactory
是CGLIB库中的一个工厂类,用于生成唯一的键对象。在CGLIB中,键对象用于在缓存中作为键,以便识别和区分不同的对象或结果。KeyFactory
通过使用一组特定的参数和算法,生成的键对象具有相同参数的唯一性,即当相同的参数传递给KeyFactory
时,它将生成相同的键对象。create()
方法:create()
方法是KeyFactory
类中的一个静态方法,用于根据传入的参数创建一个新的键实例。该方法接受一组参数作为输入,并使用这些参数生成一个唯一的键。生成键的算法通常基于参数的哈希值或其他特定逻辑,以确保生成的键是唯一的。
总:通过使用
KeyFactory
和create()
方法,可以根据特定的参数生成唯一的键对象。这些键对象可以用于在缓存中作为键,以便查找和区分不同的对象或结果。通过使用键对象,可以提高缓存的效率和准确性,避免重复计算和生成相同的结果。
public static KeyFactory create(Class keyInterface, KeyFactoryCustomizer first, List<KeyFactoryCustomizer> next) {
return create(keyInterface.getClassLoader(), keyInterface, first, next);
}
public static KeyFactory create(ClassLoader loader, Class keyInterface, KeyFactoryCustomizer customizer, List<KeyFactoryCustomizer> next) {
// 创建代理类生成器
Generator gen = new Generator();
// 设置接口为Enhancerkey类型
gen.setInterface(keyInterface);
// SPRING PATCH BEGIN
gen.setContextClass(keyInterface);
// SPRING PATCH END
// 设置定制器
if (customizer != null) {
gen.addCustomizer(customizer);
}
if (next != null && !next.isEmpty()) {
for (KeyFactoryCustomizer keyFactoryCustomizer : next) {
gen.addCustomizer(keyFactoryCustomizer);
}
}
// 设置类加载器
gen.setClassLoader(loader);
// 生成EnhancerKey对象的代理类
return gen.create();
}
3)AbstractClassGenerator
AbstractClassGenerator是CGLIB动态生成类的基类,提供了一些通用的方法和属性,用于管理和生成代理类。具体的代理类生成器(如Enhancer
和Proxy
)会继承自AbstractClassGenerator
,并根据自身的需求实现具体的方法。
create()方法:create()
方法是AbstractClassGenerator
类中的一个抽象方法,用于创建具体的代理类的实例。具体的代理类生成器(如Enhancer
和Proxy
)可以重写这个方法,根据自身的逻辑生成代理类的实例。
protected Object create(Object key) {
try {
// 根据ClassLoader获取对应ClassLoaderData
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
// 如果对应的类加载器的缓存ClassLoaderData为空,DCL模式初始对应类加载的缓存,并刷新全局缓存
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;
// 使用刚创建的ClassLoaderData对象,调用其get方法生成代理类,参数一是代理类生成器,参数二是是否使用缓存(默认开启,可以通过“cglib.useCache”指定关闭)
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
}
catch (RuntimeException | Error ex) {
throw ex;
}
catch (Exception ex) {
throw new CodeGenerationException(ex);
}
}
4)ClassLoaderData
看上面的方法中有一个很重要的类型ClassLoaderData
:是一个用于缓存和管理类加载器相关数据的类。它的作用是为了提高类加载和生成代理类的效率。
- 类加载器数据缓存:
ClassLoaderData
用于缓存已加载的类和生成的代理类。当CGLIB需要加载类或生成代理类时,首先会检查相关的ClassLoaderData
对象,以确定是否已经加载或生成过对应的类。这样可以避免重复加载和生成,提高加载和代理生成的效率。 - 类加载器隔离:不同的类加载器可以加载同一个类的不同实例,这会导致类的兼容性和实例共享的问题。通过为每个类加载器维护独立的
ClassLoaderData
,CGLIB能够在不同类加载器之间隔离已加载的类和代理类,确保各个类加载器的独立性和互不干扰。 - 缓存管理:
ClassLoaderData
还负责缓存的管理,包括缓存的清理和内存管理。它可以根据需要进行缓存的清理和释放,以减少内存占用并提高系统的整体性能。
ClassLoaderData的关键属性和方法解析如下:
/**
当使用CGLIB生成代理类时,CGLIB会首先尝试从ClassLoaderData缓存中查找是否已经存在相应的代理类。CGLIB会按照以下步骤进行查找:
1)首先,CGLIB使用ClassLoaderData对象中的类加载器(ClassLoader)来加载代理类。如果代理类已经被加载到JVM中,则代理类已经存在于缓存中,可以直接返回代理类。
2)如果代理类尚未被加载到JVM中,则CGLIB会使用ClassLoaderData对象中的类名到类对象的映射集合来查找是否已经存在相应的代理类。如果找到了相应的代理类,则代理类已经存在于缓存中,可以直接返回代理类。
3)如果在缓存中没有找到相应的代理类,则代理类尚未被生成,需要使用CGLIB生成代理类,并将代理类的信息添加到ClassLoaderData对象中。
*/
protected static class ClassLoaderData {
// 可以类比Guava库中的LoadingCache,主要是用于定义和提供缓存的功能,这里的LoadingCache主要存放了两个Function接口:GET_KEY和load,其作用的类上类上注释的作用,若generatedClasses的GET_KEY中存在对应的key,返回value,没有的话则回调load的apply方法创建
private final LoadingCache<AbstractClassGenerator, Object, Object> generatedClasses;
// GET_KEY是一个Function,接收一个代理类生成器,返回对应的key(后面会判断这个key如果为空则回调load的apply创建,如果不为空则在缓存中获取对应key的value);同时GET_KEY也是类变量,会在初始化阶段赋值!
private static final Function<AbstractClassGenerator, Object> GET_KEY = new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
return gen.key;
}
};
// 构造器,主要用来初始化generatedClasses,ClassLoader最重要的两个缓存Function对象:GET_KEY和load,load的回调就是通过代理类创建器创建代理对象
public ClassLoaderData(ClassLoader classLoader) {
if (classLoader == null) {
throw new IllegalArgumentException("classLoader == null is not yet supported");
}
this.classLoader = new WeakReference<ClassLoader>(classLoader);
Function<AbstractClassGenerator, Object> load =
new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
Class klass = gen.generate(ClassLoaderData.this);
return gen.wrapCachedClass(klass);
}
};
generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);
}
// 缓存中存在就直接获取,不存在则创建,就体现在这里!
public Object get(AbstractClassGenerator gen, boolean useCache) {
// 如果不使用缓存,则直接生成代理类的Class对象
if (!useCache) {
return gen.generate(ClassLoaderData.this);
}
// 默认使用缓存方式
else {
/*
为了提高代理类的生成效率,可以使用LoadingCache缓存已经生成的代理类。
当需要生成代理类时,首先从LoadingCache中查找是否已经存在相应的代理类,如果已经存在,则直接返回代理类;
否则,使用Enhancer对象生成代理类,并将代理类缓存到LoadingCache中:其实生成就是回调load的apply方法,最终也是执行generate方法
*/
Object cachedValue = generatedClasses.get(gen);
// 解包装并返回
return gen.unwrapCachedValue(cachedValue);
}
}
}
6)LoadingCache
LoadingCache其实就是一个缓存工具类,有使用过Guava的LoadingCache其实应该都不陌生,这里简单说一下ClassLoaderData用LoadingCache做了什么,一下为LoadingCache的简要结构:
- 构造器初始化了keyMapper和loader,其实就是ClassLoaderData里的GET_KEY和load,都是Function对象
- map存放key对应代理类Class,怎么说呢:就是说调用LoadingCache的get方法,如果keyMapper中的key在map中有值的话,就直接返回了,如果没有的话说明缓存中没有,就回调loader的对应key的apply方法创建
个人总结:这个LoadingCache就是一个二级缓存,一级缓存是key和队友的value,二级缓存是key不存在回调对应的apply方法创建,可以类比一下Spring解决循环依赖使用的三级缓存吧!问个问题:Spring解决循环依赖为什么要使用三级缓存,二级缓存不行吗?
public class LoadingCache<K, KK, V> {
protected final ConcurrentMap<KK, Object> map;
protected final Function<K, V> loader;
protected final Function<K, KK> keyMapper;
public LoadingCache(Function<K, KK> keyMapper, Function<K, V> loader) {
this.keyMapper = keyMapper;
this.loader = loader;
this.map = new ConcurrentHashMap();
}
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);
}
7)Enhancer
当我们创建完Enhancer对象,再设置完对应的superClass和callback,接下来就是调用create
方法创建我们需要的代理对象了!可以看到是使用EnhancerKey的代理对象创建了对应的key,然后同样的调用AbstractClassGenerator的create方法完成代理对象创建,过程和3一致的,只是前面的创建是创建EnhancerKey的代理对象,这里创建的是我们需要的代理对象,而EnhancerKey这个对象对外部使用者来说是隐藏的!最终创建完成后返回代理对象。
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;
}
8)EnhancerKey代理类结构
通过上面的步骤查看到Enhancer enhancer = new Enhancer()
这一行代码的执行流程,最终结果是为EnhancerKey生成其代理对象,虽然这个EnhancerKey对象对于CGLIB的使用者来说是无感的,但是想要深入理解CGLIB还是需要了解一下该类型结构,通过idea(也可以通过其他的反编译工具)查看该代理类的结构如下,其主要功能如下:
- 仅提供了newInstance方法,即当我们执行enhancer.create()的时候其实就是调用代理类中的newInstance方法(KEY_FACTORY.newInstance),并为属性
FIELD_0 ~ FIELD_6
赋初值,另外就是创建对应的EnhancerKey对象 - 重写了equal和hashCode方法,重写这两个方法的目的很明确了吧,就是做对象是否相同的判断,所以LoadingCache里面的map中key是否存在其实就是这样判断的!具体的判断相同逻辑就不说了(自行看equal和hashCode方法),反正就是保证了同一个类加载器的相同类型的对象需要创建代理对象都是使用的同一个代理类claass文件
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.cglib.proxy;
import org.springframework.asm.Type;
import org.springframework.cglib.core.KeyFactory;
import org.springframework.cglib.core.WeakCacheKey;
import org.springframework.cglib.proxy.Enhancer.EnhancerKey;
public class Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f extends KeyFactory implements EnhancerKey {
private final String FIELD_0;
private final String[] FIELD_1;
private final WeakCacheKey FIELD_2;
private final Type[] FIELD_3;
private final boolean FIELD_4;
private final boolean FIELD_5;
private final Long FIELD_6;
public Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f() {
}
public Object newInstance(String var1, String[] var2, WeakCacheKey var3, Type[] var4, boolean var5, boolean var6, Long var7) {
return new Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f(var1, var2, var3, var4, var5, var6, var7);
}
public Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f(String var1, String[] var2, WeakCacheKey var3, Type[] var4, boolean var5, boolean var6, Long var7) {
this.FIELD_0 = var1;
this.FIELD_1 = var2;
this.FIELD_2 = var3;
this.FIELD_3 = var4;
this.FIELD_4 = var5;
this.FIELD_5 = var6;
this.FIELD_6 = var7;
}
public int hashCode() {
int var10002 = 1213 * 1209107;
String var10001 = this.FIELD_0;
int var10000 = var10002 + (var10001 != null ? var10001.hashCode() : 0);
String[] var5 = this.FIELD_1;
if (var5 != null) {
String[] var1 = var5;
for(int var2 = 0; var2 < var1.length; ++var2) {
var10000 = var10000 * 1209107 + (var1[var2] != null ? var1[var2].hashCode() : 0);
}
}
var10002 = var10000 * 1209107;
WeakCacheKey var6 = this.FIELD_2;
var10000 = var10002 + (var6 != null ? var6.hashCode() : 0);
Type[] var7 = this.FIELD_3;
if (var7 != null) {
Type[] var3 = var7;
for(int var4 = 0; var4 < var3.length; ++var4) {
var10000 = var10000 * 1209107 + (var3[var4] != null ? var3[var4].hashCode() : 0);
}
}
var10002 = ((var10000 * 1209107 + (this.FIELD_4 ^ 1)) * 1209107 + (this.FIELD_5 ^ 1)) * 1209107;
Long var8 = this.FIELD_6;
return var10002 + (var8 != null ? var8.hashCode() : 0);
}
public boolean equals(Object var1) {
if (var1 instanceof Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f) {
String var10000 = this.FIELD_0;
String var10001 = ((Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f)var1).FIELD_0;
if (var10001 == null) {
if (var10000 != null) {
return false;
}
} else if (var10000 == null || !var10000.equals(var10001)) {
return false;
}
String[] var8 = this.FIELD_1;
String[] var10 = ((Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f)var1).FIELD_1;
if (var10 == null) {
if (var8 != null) {
return false;
}
} else {
label178: {
if (var8 != null) {
if (var10.length == var8.length) {
String[] var2 = var10;
String[] var3 = var8;
int var4 = 0;
while(true) {
if (var4 >= var2.length) {
break label178;
}
var10000 = var2[var4];
var10001 = var3[var4];
if (var3[var4] == null) {
if (var10000 != null) {
return false;
}
} else if (var10000 == null || !var10000.equals(var10001)) {
return false;
}
++var4;
}
}
}
return false;
}
}
WeakCacheKey var9 = this.FIELD_2;
WeakCacheKey var13 = ((Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f)var1).FIELD_2;
if (var13 == null) {
if (var9 != null) {
return false;
}
} else if (var9 == null || !var9.equals(var13)) {
return false;
}
Type[] var11 = this.FIELD_3;
Type[] var15 = ((Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f)var1).FIELD_3;
if (var15 == null) {
if (var11 != null) {
return false;
}
} else {
if (var11 == null) {
return false;
}
if (var15.length != var11.length) {
return false;
}
Type[] var5 = var15;
Type[] var6 = var11;
for(int var7 = 0; var7 < var5.length; ++var7) {
Type var12 = var5[var7];
Type var16 = var6[var7];
if (var6[var7] == null) {
if (var12 != null) {
return false;
}
} else if (var12 == null || !var12.equals(var16)) {
return false;
}
}
}
if (this.FIELD_4 == ((Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f)var1).FIELD_4 && this.FIELD_5 == ((Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f)var1).FIELD_5) {
Long var14 = this.FIELD_6;
Long var17 = ((Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$4ce19e8f)var1).FIELD_6;
if (var17 == null) {
if (var14 == null) {
return true;
}
} else if (var14 != null && var14.equals(var17)) {
return true;
}
}
}
return false;
}
public String toString() {
StringBuffer var10000 = new StringBuffer();
String var10001 = this.FIELD_0;
var10000 = (var10001 != null ? var10000.append(var10001.toString()) : var10000.append("null")).append(", ");
String[] var6 = this.FIELD_1;
if (var6 != null) {
var10000 = var10000.append("{");
String[] var1 = var6;
for(int var2 = 0; var2 < var1.length; ++var2) {
var10000 = (var1[var2] != null ? var10000.append(var1[var2].toString()) : var10000.append("null")).append(", ");
}
var10000.setLength(var10000.length() - 2);
var10000 = var10000.append("}");
} else {
var10000 = var10000.append("null");
}
var10000 = var10000.append(", ");
WeakCacheKey var9 = this.FIELD_2;
var10000 = (var9 != null ? var10000.append(var9.toString()) : var10000.append("null")).append(", ");
Type[] var10 = this.FIELD_3;
if (var10 != null) {
var10000 = var10000.append("{");
Type[] var3 = var10;
for(int var4 = 0; var4 < var3.length; ++var4) {
var10000 = (var3[var4] != null ? var10000.append(var3[var4].toString()) : var10000.append("null")).append(", ");
}
var10000.setLength(var10000.length() - 2);
var10000 = var10000.append("}");
} else {
var10000 = var10000.append("null");
}
var10000 = var10000.append(", ").append(this.FIELD_4).append(", ").append(this.FIELD_5).append(", ");
Long var13 = this.FIELD_6;
return (var13 != null ? var10000.append(var13.toString()) : var10000.append("null")).toString();
}
}
9)代理类的生成
从上面可以看到不管是使用缓存与否,生成代理类都是使用了AbstractClassGenerator#generate方法,这个方法没什么好说的,我也没怎么看,反正底层就是使用ASM工具库生成对应的代理类Class文件,生成之前无非就是获取一下要生成的类名、包名、类加载器之类的准备工作
protected Class generate(ClassLoaderData data) {
Class gen;
Object save = CURRENT.get();
CURRENT.set(this);
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) {
// 生成代理类名字,cglib的代理类名字有$$就是这里生成的
String name = generateClassName(data.getUniqueNamePredicate());
data.reserveName(name);
this.setClassName(name);
}
if (attemptLoad) {
try {
gen = classLoader.loadClass(getClassName());
return gen;
}
catch (ClassNotFoundException e) {
// ignore
}
}
// 生成字节码,就不深入探索了,没有意义
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
synchronized (classLoader) { // just in case
// SPRING PATCH BEGIN
// 获取Class文件
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain, contextClass);
// SPRING PATCH END
}
// 返回代理类Class
return gen;
}
catch (RuntimeException | Error ex) {
throw ex;
}
catch (Exception ex) {
throw new CodeGenerationException(ex);
}
finally {
CURRENT.set(save);
}
}
10)代理类方法执行
前面已经了解到了代理对象的创建,但是怎样调用代理对象的方法呢?上一篇文章中说到JDK是实现了InvocationHandler接口,然后代理类构造器中初始化这个实例变量回调处理器,并初始化了原始类中所有方法的Method对象,然后执行代理对象的方法的时候就是回调到实现的处理器中,把代理对象、方法对象、参数列表等信息等回调回去,然后通过反射执行具体运行时对象的方法
那么看看CGLIB的代理类结构,使用IDEA打开生成的代理类,截取一小部分关键信息,可以看到一下结论:
- 代理类实现了原始类UserService,并重写了
add
和get
方法(get方法没粘贴出来) - 代理类为原始类中的
add
和get
方法都创建了对应的MethodProxy对象,那么这个MethodProxy对象究竟是什么? - 看到了我们实现的UserServiceInterceptor是如何回调回去的了
public class UserService$$EnhancerByCGLIB$$7bffb10 extends UserService implements Factory {
// 当前代理对象add方法对应的MethodProxy对象
private static final MethodProxy CGLIB$add$0$Proxy;
// 当前代理对象get的MethodProxy对象
private static final MethodProxy CGLIB$get$1$Proxy;
static {
CGLIB$STATICHOOK1();
}
static void CGLIB$STATICHOOK1() {
CGLIB$add$0$Proxy = MethodProxy.create(var1, var0, "(Lcom/lin/tesia/entity/User;)I", "add", "CGLIB$add$0");
CGLIB$get$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Lcom/lin/tesia/entity/User;", "get", "CGLIB$get$1");
}
final int CGLIB$add$0(User var1) {
return super.add(var1);
}
// 同理get方法也是差不多的
public final int add(User var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
/**
在这里可以看到我们前面实现的UserServiceInterceptor是如何回调回去的了吧,哈哈哈
参数一:当前代理对象
参数二:原始对象的add方法method对象
参数三:形参列表
参数四:当前代理对象add方法对应的MethodProxy对象
*/
Object var2 = var10000.intercept(this, CGLIB$add$0$Method, new Object[]{var1}, CGLIB$add$0$Proxy);
return var2 == null ? 0 : ((Number)var2).intValue();
} else {
return super.add(var1);
}
}
上面了解到的信息都指向了需要知道MethodProxy究竟是什么,为什么调用的是MethodProxy的invokeSuper方法?从代理类中看到创建MethodProxy调用的是其静态create方法,其他两个关键的方法是invoke和invokeSuper
/**
*
* @param c1 原始类模板
* @param c2 代理类模板
* @param desc 方法描述
* @param name1 原始对象的方法名
* @param name2 代理对象的方法名
* @return MethodProxy
*/
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;
}
/**
* 调用代理类原始方法即上面代理类重写的add方法,但是运行时对象是代理对象
* 查看上面代理类的结构可以发现这里可能会出现无限递归回调的情况,导致StackOverflowError异常!因为代理类 的add方法会一直回调回去我们实现的UserServiceInterceptor拦截器,所以知道为什么调用invokeSuper了吧
*/
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw ex;
}
}
/**
* 这里调用的就是代理类的CGLIB$add$0方法,这个方法里直接调用super.add方法即UserService的add方法,不会出现递归异常的情况
*/
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
}
catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
private void init() {
if (fastClassInfo == null) {
synchronized (initLock) {
if (fastClassInfo == null) {
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
}
}
}
不管是invoke方法还是invokeSuper方法都会先执行init
方法,该方法是初始化FastClass,那么FastClass又是什么呢?这里直接说结论了,不深入探究FastClass:CGLIB的FastClass
是一个用于快速调用方法的类。它是CGLIB库中的一部分,用于绕过Java反射的性能开销,提高方法调用的效率。FastClass
的主要功能是为目标类生成一个方法索引表,以便在运行时快速定位和调用目标类的方法,而无需使用Java的反射机制。由于FastClass
绕过了Java的反射机制,它能够大幅提高方法调用的性能,特别适用于频繁调用的场景,比如动态代理和AOP等。FastClass
的工作原理如下:
- 通过CGLIB字节码技术,为目标类生成一个专门的类,该类包含了目标类中的所有方法的索引和调用逻辑。
- 在目标类的代理类中,使用
FastClass
的实例来调用方法,而不是通过Java的反射机制。 - 在方法调用时,
FastClass
会根据方法索引直接定位到对应的方法(getIndex方法),并执行方法的调用(invoke方法)。
需要注意的是,每个目标类都会对应一个FastClass
实例,因此在创建代理类时,CGLIB会为每个目标类生成一个对应的FastClass
实例,并将其与代理类关联起来。这样,在代理类中就可以使用FastClass
实例来执行目标类的方法调用,实现了高效的方法调用。
其代理类结构就是上面生成的三个代理类中其余的两个文件,一个是原始类UserService的FastClss,另一个是UserService代理类的FastClass,其内部结构就是下面这样,通过getIndex方法确认要调用的方法,然后执行invoke方法!
11)总结
创建过程:CGLIB动态代理实现是基于ASM工具直接操作字节码,所以要考虑到充分使用缓存,避免重复类型对象的创建而重复创建相同的代理类字节码文件。所以Enhancer类中有一个类变量EnhancerKey对象随着类的加载而加载(只加载一次),该对象的作用是用来生成代理类缓存中使用到的键值,用来避免重复创建,也是一个代理对象。然后会在第一次创建EnhancerKey代理对象的时候初始化了对应的ClassLoaderData缓存,该缓存主要有两个很重要的Function对象,一个是GET_KEY,一个是load。这两个function对象的主要作用是:
- GET_KEY主要用来存放代理类生成器和对应的EnhancerKey值,如果对应的缓存在在LoadingCache的map中存在value,则属于重复创建,直接返回之前的对象
- 如果value为空,则回调load中对应的Function对象的apply方法进行创建,这个方法最终调用的是和不使用缓存一样的AbstractClassGenerator的generate方法,创建完成后还会保存到LoadingCache的map中,下次就不用重复创建
调用过程:通过使用MethodProxy
和FastClass
来绕过反射,直接调用代理类重写了原始类的方法
具体过程可以查看上面的详细说明
5 Cglib和Jdk动态代理的区别
- 基于接口 vs. 基于类:
- JDK动态代理:JDK动态代理是基于接口的代理技术。它要求目标对象实现至少一个接口,代理类通过实现同样的接口来代理目标对象。JDK动态代理使用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来创建和管理代理类。 - CGLIB动态代理:CGLIB动态代理是基于类的代理技术。它可以代理没有实现接口的类,直接操作目标类的字节码。CGLIB动态代理使用CGLIB库来生成代理类,通过继承目标类并重写其方法来实现代理。
- JDK动态代理:JDK动态代理是基于接口的代理技术。它要求目标对象实现至少一个接口,代理类通过实现同样的接口来代理目标对象。JDK动态代理使用
- 生成代理类的方式:
- JDK动态代理:JDK动态代理通过在运行时生成代理类的字节码。它使用Java的反射机制来创建和操作代理类,可以动态生成代理类的实例。
- CGLIB动态代理:CGLIB动态代理通过继承目标类并生成其子类的方式来创建代理类。它使用CGLIB库来生成代理类的字节码,并在运行时通过修改字节码来实现代理。
- 目标对象类型限制:
- JDK动态代理:JDK动态代理要求目标对象实现接口,因此只能代理实现了接口的类。如果目标对象没有实现接口,则无法使用JDK动态代理。
- CGLIB动态代理:CGLIB动态代理没有对目标对象类型的限制,它可以代理任何类,包括没有实现接口的类。
- 性能:
- JDK动态代理:由于使用了Java的反射机制对方法调用进行拦截和处理,方法调用的性能相对较低。
- CGLIB动态代理:通过使用
MethodProxy
和FastClass
来绕过反射,避免了反射的性能开销,方法调用的性能较高。但是使用ASM直接操作字节码文件的过程性能开销又比较大,但是CGLIB又充分使用了二级缓存,避免了重复类型的代理对象的创建而去重复创建对应的字节码文件,但是我感觉这个没啥用,在Spring容器中使用AOP代理基本都是单例的,也不会又原型吧···