文章目录
- 一、CGLIB 代理简介
- 1.1 CGLIB 代理的基本原理和特点
- 1.2 分析 CGLIB 如何通过字节码技术创建代理类
- 二、深入分析 CglibAopProxy 类的结构
- 2.1 CglibAopProxy 类结构
- 2.2 CglibAopProxy 类源码
- 三、CGLIB 代理对象的创建过程
- 3.1 配置 Enhancer 生成代理对象
- 3.2 探讨如何通过字节码生成技术嵌入拦截器逻辑到代理类中
- 四、CGLIB 代理链的处理
- 4.1 拦截器的调用顺序
- 4.2 实现拦截器具体逻辑
- 五、实践与应用
- 5.1 编写自定义的 CGLIB 拦截器
- 5.2 实现对非接口类的代理和增强功能
一、CGLIB 代理简介
1.1 CGLIB 代理的基本原理和特点
CGLIB是一个强大的、高性能的代码生成库。它被广泛应用于AOP(面向切面编程)、ORM(对象关系映射)和其他一些框架中。
CGLIB代理的基本原理:
- 创建代理类:CGLIB通过
ASM
字节码操作框架,在运行时动态生成目标类的子类。这个子类会继承自目标类。 - 方法拦截:在生成的子类中,会覆盖所有非
final
的方法。覆盖的方法会委托给一个用户定义的拦截器(MethodInterceptor
),拦截器中包含了增强的代码。 - 调用流程:当调用代理类的方法时,实际上是在调用被覆盖的方法。这些方法内部会调用拦截器,拦截器再去调用原始类的相应方法。
CGLIB代理的特点:
- 无需接口:CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
- 性能:CGLIB生成的代理类是目标类的子类,相比于JDK动态代理(接口代理),CGLIB代理通常有更好的性能,因为它直接调用父类的方法,减少了反射调用的开销。
- 灵活性:由于CGLIB代理是通过继承实现的,它无法代理final类和方法。但是,它提供了比JDK代理更高的灵活性,因为它可以代理任何类,而不受接口限制。
- 复杂性:CGLIB代理的实现比JDK动态代理复杂,因为它涉及到字节码生成和类加载机制。
- 兼容性:CGLIB代理通常与Spring框架结合使用,Spring AOP默认使用JDK动态代理,但如果目标对象没有实现接口,Spring AOP会自动切换到CGLIB代理。
1.2 分析 CGLIB 如何通过字节码技术创建代理类
CGLIB通过操纵字节码,创建出目标类的子类,并在子类中覆盖非final的方法,从而实现方法拦截和增强。
CGLIB创建代理类的基本步骤:
- 确定目标类:首先要确定需要被代理的目标类。
CGLIB
代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。 - 创建Enhancer对象:
Enhancer
是CGLIB
中的一个核心类,用于创建代理类。首先创建一个Enhancer
实例,并设置其父类(即目标类)。 - 设置Callback:
Callback
是一个接口,用于定义代理类中覆盖方法的逻辑。通常使用MethodInterceptor
接口,它允许我们在调用原始方法之前和之后插入自定义代码。将实现的Callback
对象设置给Enhancer
。 - 创建代理类:调用
Enhancer
的create()
方法,CGLIB
会使用ASM
字节码操作框架来动态生成一个继承自目标类的子类。这个子类会覆盖所有非final
的方法,并将调用委托给Callback
对象。 - 使用代理类:
create()
方法返回的是一个代理类的实例,这个实例可以被当作目标类的实例来使用。当调用代理类的方法时,实际上会调用MethodInterceptor
中的intercept()
方法。 - 方法调用流程:在
intercept()
方法中,可以调用Method
对象的invoke()
方法来执行原始方法。这样,我们就可以在原始方法执行前后插入自定义的逻辑,实现方法的拦截和增强。
二、深入分析 CglibAopProxy 类的结构
2.1 CglibAopProxy 类结构
- 成员变量:
AdvisedSupport advised
:存储了AOP配置信息的数据结构,如目标对象、切面等。Callback callback
:CGLIB 回调对象,负责实现代理逻辑。
- 构造方法:
CglibAopProxy(AdvisedSupport config)
:构造方法接收一个AdvisedSupport
参数,用于设置AOP配置信息。
- 核心方法:
getProxy(ClassLoader classLoader)
:生成代理对象的核心方法,接收一个 ClassLoader 参数用于加载代理类。createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks)
:使用 CGLIB 的 Enhancer 创建代理类,并返回代理对象的实例。proxy(ClassLoader classLoader, Callback[] callbacks)
:创建代理类并生成代理对象的实现逻辑, 使用 Enhancer 创建代理类,并指定 Callback 对象,完成代理类的生成和实例化。createEnhancer()
:创建 Enhancer 对象,用于生成代理类, Enhancer 是 CGLIB 中负责生成代理类的核心类。
2.2 CglibAopProxy 类源码
仅展示部分源码,其它源码会在下方解决其它问题时会出现,没有出现的,读者感兴趣可以自行去解读源码。
三、CGLIB 代理对象的创建过程
代理对象的创建过程: 检查是否可以使用缓存的代理对象 -> 准备 CGLIB Enhancer -> 配置 Enhancer -> 设置回调处理器(Callback) -> 生成代理类字节码 -> 创建代理对象实例 -> 将代理对象缓存起来
3.1 配置 Enhancer 生成代理对象
Enhancer
对象通过调用 create()
方法来生成代理对象。
createHelper()
方法用来实际创建代理对象。
AbstractClassGenerator
对象通过调用 create()
方法,根据给定的键值(key)
创建对象实例。
3.2 探讨如何通过字节码生成技术嵌入拦截器逻辑到代理类中
createProxyClassAndInstance
负责创建代理类的实例,使用 CGLIB
技术创建代理对象,并将指定的拦截器回调方法应用于代理对象上。
四、CGLIB 代理链的处理
通过
ReflectiveMethodInvocation
类了解到在 Spring 框架中如何构建和执行代理链,以及拦截器如何在拦截器链中协作,以实现对目标方法的拦截和处理。
4.1 拦截器的调用顺序
在拦截器链中如何依次执行拦截器,并通过判断和调用不同的拦截器或目标方法来实现拦截和处理逻辑。
invokeJoinpoint()
用于执行目标方法,如果拦截器链中已经没有下一个拦截器了,或者拦截器中的某个拦截器选择不继续执行拦截器链,那么就会调用这个方法来执行目标方法。
4.2 实现拦截器具体逻辑
定义了方法拦截器的标准,任何实现该接口的类都可以作为 Spring AOP
中的拦截器,用于在目标方法执行前后添加额外的逻辑。
五、实践与应用
5.1 编写自定义的 CGLIB 拦截器
假设有一个简单的服务类
UserService
,其中包含一些方法,希望能够在调用这些方法之前和之后记录日志。使用CGLIB来实现一个拦截器,记录方法调用的开始和结束时间。
- 服务类 UserService,模拟创建和更新用户信息。
public class UserService {
public void createUser(String username) {
System.out.println("Creating user: " + username);
// 模拟创建用户的逻辑
}
public void updateUser(String username) {
System.out.println("Updating user: " + username);
// 模拟更新用户的逻辑
}
}
- 自定义的CGLIB拦截器,用于记录方法调用的开始和结束时间。
/**
* 创建 LoggingInterceptor 类,实现 MethodInterceptor 接口
*/
public class LoggingInterceptor implements MethodInterceptor {
/**
* 参数:obj 是被代理的对象实例
* method 是被调用的方法对象
* args 是方法的参数数组
* proxy 是用于调用父类(被代理类)方法的代理对象
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 获取方法调用开始时的时间戳
long startTime = System.currentTimeMillis();
System.out.println("Method " + method.getName() + " start at: " + startTime);
// 调用被代理类的原始方法,而不是代理对象的方法,以避免循环调用
Object result = proxy.invokeSuper(obj, args);
// 获取方法调用结束时的时间戳
long endTime = System.currentTimeMillis();
System.out.println("Method " + method.getName() + " end at: " + endTime);
// 方法执行所花费的时间
System.out.println("Method " + method.getName() + " execution time: " + (endTime - startTime) + " milliseconds");
// 调用原始方法后的返回值
return result;
}
public static void main(String[] args) {
UserService userService = new UserService();
// 使用CGLIB的 Enhancer 类创建了 UserService 类的代理对象,并将拦截器设置为回调方法
Enhancer enhancer = new Enhancer();
// 设置了要代理的目标类是 UserService
enhancer.setSuperclass(UserService.class);
// 指定了在方法调用时应该执行的拦截逻辑
enhancer.setCallback(new LoggingInterceptor());
// 创建代理对象,将会在方法调用时执行我们定义的拦截逻辑
UserService userServiceProxy = (UserService) enhancer.create();
// 调用代理对象的 createUser 和 updateUser 方法来触发拦截器的拦截逻辑
userServiceProxy.createUser("John Doe");
userServiceProxy.updateUser("Jane Smith");
}
}
//输出结果:
Method createUser start at: 1621802728000
Creating user: John Doe
Method createUser end at: 1621802728000
Method createUser execution time: 0 milliseconds
Method updateUser start at: 1621802728000
Updating user: Jane Smith
Method updateUser end at: 1621802728000
Method updateUser execution time: 0 milliseconds
5.2 实现对非接口类的代理和增强功能
实现对非接口类的代理和增强功能通常使用 Spring AOP来实现,提供了一种便捷的方式来在方法执行前、执行后、方法抛出异常时等时机插入特定逻辑,而无需修改原始类的代码。
假设有一个订单管理系统,其中包含一个 OrderService 类,该类负责处理订单相关的业务逻辑,比如创建订单、更新订单状态等。希望在处理订单相关业务时,记录日志并统计方法执行时间。
- 切面类 OrderAspect。
@Aspect
@Component
public class OrderAspect {
/**
* 切面方法,用于实现切面的逻辑 -> 表示正在执行目标方法之前
* 接受一个 JoinPoint 参数,连接点 -> 被增强的目标方法
*/
@Before("execution(* com.example.service.OrderService.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before executing method: " + joinPoint.getSignature());
}
/**
* 切面方法,用于实现切面的逻辑 -> 表示目标方法执行完成后
* 接受一个 JoinPoint 参数,连接点 -> 被增强的目标方法
*/
@After("execution(* com.example.service.OrderService.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After executing method: " + joinPoint.getSignature());
}
}
- 配置类中启用 Spring AOP 功能。
/**
* 标识这个类是一个配置类 -> 告诉 Spring 容器如何配置应用程序上下文
* 启用了 AspectJ 自动代理 -> 告诉 Spring 在运行时生成 AOP 代理以支持 @AspectJ 切面
* 指示 Spring 在包 com.example 及其子包中扫描组件 -> 自动发现并注册带有 @Component、@Service、@Repository 和 @Controller 注解的 bean
*/
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
- OrderService 类。
/**
* 调用 OrderService 类的 createOrder() 或 updateOrderStatus() 方法时,OrderAspect 切面中定义的增强逻辑会在方法执行前后生效,从而实现了对非接口类的代理和增强功能
*/
@Service
public class OrderService {
public void createOrder() {
// 模拟创建订单的业务逻辑
System.out.println("Creating order...");
}
public void updateOrderStatus() {
// 模拟更新订单状态的业务逻辑
System.out.println("Updating order status...");
}
}
// 输出结果:
Before executing method: public void com.example.service.OrderService.createOrder()
Creating order...
After executing method: public void com.example.service.OrderService.createOrder()
Before executing method: public void com.example.service.OrderService.updateOrderStatus()
Updating order status...
After executing method: public void com.example.service.OrderService.updateOrderStatus()
对乐于苦斗的人来说,苦斗不是憾事,而是乐事