0


深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

在这里插入图片描述

文章目录

一、CGLIB 代理简介

1.1 CGLIB 代理的基本原理和特点

CGLIB是一个强大的、高性能的代码生成库。它被广泛应用于AOP(面向切面编程)、ORM(对象关系映射)和其他一些框架中。

CGLIB代理的基本原理

  1. 创建代理类:CGLIB通过ASM字节码操作框架,在运行时动态生成目标类的子类。这个子类会继承自目标类。
  2. 方法拦截:在生成的子类中,会覆盖所有非final的方法。覆盖的方法会委托给一个用户定义的拦截器(MethodInterceptor),拦截器中包含了增强的代码。
  3. 调用流程:当调用代理类的方法时,实际上是在调用被覆盖的方法。这些方法内部会调用拦截器,拦截器再去调用原始类的相应方法。

CGLIB代理的特点

  1. 无需接口:CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
  2. 性能:CGLIB生成的代理类是目标类的子类,相比于JDK动态代理(接口代理),CGLIB代理通常有更好的性能,因为它直接调用父类的方法,减少了反射调用的开销。
  3. 灵活性:由于CGLIB代理是通过继承实现的,它无法代理final类和方法。但是,它提供了比JDK代理更高的灵活性,因为它可以代理任何类,而不受接口限制。
  4. 复杂性:CGLIB代理的实现比JDK动态代理复杂,因为它涉及到字节码生成和类加载机制。
  5. 兼容性:CGLIB代理通常与Spring框架结合使用,Spring AOP默认使用JDK动态代理,但如果目标对象没有实现接口,Spring AOP会自动切换到CGLIB代理。

1.2 分析 CGLIB 如何通过字节码技术创建代理类

CGLIB通过操纵字节码,创建出目标类的子类,并在子类中覆盖非final的方法,从而实现方法拦截和增强。

CGLIB创建代理类的基本步骤

  1. 确定目标类:首先要确定需要被代理的目标类。CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
  2. 创建Enhancer对象EnhancerCGLIB中的一个核心类,用于创建代理类。首先创建一个Enhancer实例,并设置其父类(即目标类)。
  3. 设置CallbackCallback是一个接口,用于定义代理类中覆盖方法的逻辑。通常使用MethodInterceptor接口,它允许我们在调用原始方法之前和之后插入自定义代码。将实现的Callback对象设置给Enhancer
  4. 创建代理类:调用Enhancercreate()方法,CGLIB会使用ASM字节码操作框架来动态生成一个继承自目标类的子类。这个子类会覆盖所有非final的方法,并将调用委托给Callback对象。
  5. 使用代理类create()方法返回的是一个代理类的实例,这个实例可以被当作目标类的实例来使用。当调用代理类的方法时,实际上会调用MethodInterceptor中的intercept()方法。
  6. 方法调用流程:在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来实现一个拦截器,记录方法调用的开始和结束时间。

  1. 服务类 UserService,模拟创建和更新用户信息。
publicclassUserService{publicvoidcreateUser(String username){System.out.println("Creating user: "+ username);// 模拟创建用户的逻辑}publicvoidupdateUser(String username){System.out.println("Updating user: "+ username);// 模拟更新用户的逻辑}}
  1. 自定义的CGLIB拦截器,用于记录方法调用的开始和结束时间。
/**
 * 创建 LoggingInterceptor 类,实现 MethodInterceptor 接口
 */publicclassLoggingInterceptorimplementsMethodInterceptor{/**
     * 参数:obj 是被代理的对象实例
     *      method 是被调用的方法对象
     *      args 是方法的参数数组
     *      proxy 是用于调用父类(被代理类)方法的代理对象
     */publicObjectintercept(Object obj,Method method,Object[] args,MethodProxy proxy)throwsThrowable{// 获取方法调用开始时的时间戳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;}publicstaticvoidmain(String[] args){UserService userService =newUserService();// 使用CGLIB的 Enhancer 类创建了 UserService 类的代理对象,并将拦截器设置为回调方法Enhancer enhancer =newEnhancer();// 设置了要代理的目标类是 UserService
        enhancer.setSuperclass(UserService.class);// 指定了在方法调用时应该执行的拦截逻辑
        enhancer.setCallback(newLoggingInterceptor());// 创建代理对象,将会在方法调用时执行我们定义的拦截逻辑UserService userServiceProxy =(UserService) enhancer.create();// 调用代理对象的 createUser 和 updateUser 方法来触发拦截器的拦截逻辑
        userServiceProxy.createUser("John Doe");
        userServiceProxy.updateUser("Jane Smith");}}//输出结果:Method createUser start at:1621802728000Creating user:JohnDoeMethod createUser end at:1621802728000Method createUser execution time:0 milliseconds
Method updateUser start at:1621802728000Updating user:JaneSmithMethod updateUser end at:1621802728000Method updateUser execution time:0 milliseconds

5.2 实现对非接口类的代理和增强功能

实现对非接口类的代理和增强功能通常使用 Spring AOP来实现,提供了一种便捷的方式来在方法执行前、执行后、方法抛出异常时等时机插入特定逻辑,而无需修改原始类的代码。

假设有一个订单管理系统,其中包含一个 OrderService 类,该类负责处理订单相关的业务逻辑,比如创建订单、更新订单状态等。希望在处理订单相关业务时,记录日志并统计方法执行时间。

  1. 切面类 OrderAspect。
@Aspect@ComponentpublicclassOrderAspect{/**
     * 切面方法,用于实现切面的逻辑 -> 表示正在执行目标方法之前
     * 接受一个 JoinPoint 参数,连接点 -> 被增强的目标方法
     */@Before("execution(* com.example.service.OrderService.*(..))")publicvoidlogBefore(JoinPoint joinPoint){System.out.println("Before executing method: "+ joinPoint.getSignature());}/**
     * 切面方法,用于实现切面的逻辑 -> 表示目标方法执行完成后
     * 接受一个 JoinPoint 参数,连接点 -> 被增强的目标方法
     */@After("execution(* com.example.service.OrderService.*(..))")publicvoidlogAfter(JoinPoint joinPoint){System.out.println("After executing method: "+ joinPoint.getSignature());}}
  1. 配置类中启用 Spring AOP 功能。
/**
 * 标识这个类是一个配置类 -> 告诉 Spring 容器如何配置应用程序上下文
 * 启用了 AspectJ 自动代理 -> 告诉 Spring 在运行时生成 AOP 代理以支持 @AspectJ 切面
 * 指示 Spring 在包 com.example 及其子包中扫描组件 -> 自动发现并注册带有 @Component、@Service、@Repository 和 @Controller 注解的 bean
 */@Configuration@EnableAspectJAutoProxy@ComponentScan(basePackages ="com.example")publicclassAppConfig{}
  1. OrderService 类。
/**
 * 调用 OrderService 类的 createOrder() 或 updateOrderStatus() 方法时,OrderAspect 切面中定义的增强逻辑会在方法执行前后生效,从而实现了对非接口类的代理和增强功能
 */@ServicepublicclassOrderService{publicvoidcreateOrder(){// 模拟创建订单的业务逻辑System.out.println("Creating order...");}publicvoidupdateOrderStatus(){// 模拟更新订单状态的业务逻辑System.out.println("Updating order status...");}}// 输出结果:Before executing method:publicvoidcom.example.service.OrderService.createOrder()Creating order...After executing method:publicvoidcom.example.service.OrderService.createOrder()Before executing method:publicvoidcom.example.service.OrderService.updateOrderStatus()Updating order status...After executing method:publicvoidcom.example.service.OrderService.updateOrderStatus()
对乐于苦斗的人来说,苦斗不是憾事,而是乐事
标签: spring java 后端

本文转载自: https://blog.csdn.net/qq_51601665/article/details/139240358
版权归原作者 忆~遂愿 所有, 如有侵权,请联系我们删除。

“深度解析 Spring 源码:探秘 CGLIB 代理的奥秘”的评论:

还没有评论