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

在这里插入图片描述

文章目录

    • 一、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代理的基本原理

  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,模拟创建和更新用户信息。
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);// 模拟更新用户的逻辑}
}
  1. 自定义的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();// 设置了要代理的目标类是 UserServiceenhancer.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 类,该类负责处理订单相关的业务逻辑,比如创建订单、更新订单状态等。希望在处理订单相关业务时,记录日志并统计方法执行时间。

  1. 切面类 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());}
}
  1. 配置类中启用 Spring AOP 功能。
/*** 标识这个类是一个配置类 -> 告诉 Spring 容器如何配置应用程序上下文* 启用了 AspectJ 自动代理 -> 告诉 Spring 在运行时生成 AOP 代理以支持 @AspectJ 切面* 指示 Spring 在包 com.example 及其子包中扫描组件 -> 自动发现并注册带有 @Component、@Service、@Repository 和 @Controller 注解的 bean*/
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example")
public class AppConfig {}
  1. 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()

对乐于苦斗的人来说,苦斗不是憾事,而是乐事

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/16670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PageHelper分页查询时,count()查询记录总数与实际返回的数据数量不一致

目录 场景简介代码判断异常情况排查原因解决 场景简介 1、使用PageHelper进行分页查询 2、最终构建PageInfo对象时,total与实际数据量不符 代码判断 异常情况 排查 通过对比count()查询的SQL与查询记录的SQL,发现是PageHelper分页查询时省去了order b…

Linux系统编程——基础IO与文件描述符(管理已打开的内存文件)

目录 一,文件预备 二,C语言文件操作函数 2.1 默认打开的三个流 2.2 写文件 2.3 读文件 2.4 再次理解当前路径 三,Linux操作文件系统调用 3.1 open()和close() 3.1.1 第一个参数 3.1.2 *第二个参数 3.1.3 第三个参数 3.2 write(…

(2024,基于熵的激活函数动态优化,具有边界条件的最差激活函数,修正正则化 ReLU)寻找更优激活函数

A Method on Searching Better Activation Functions 公众号:EDPJ(进 Q 交流群:922230617 或加 VX:CV_EDPJ 进 V 交流群) 目录 0. 摘要 3. 动机 4. 方法论 4.1 问题设定 4.1.1 贝叶斯错误率和信息熵 4.1.2 激活…

host修改

前言 想要修改 hosts 文件,您需要具有对系统文件的适当访问权限,并且知道如何编辑文本文件。hosts 文件是一个用于域名解析的本地文件,它允许您为特定的 IP 地址指定主机名。 以下是在不同操作系统中修改 hosts 文件的步骤: 一、…

香橙派 Kunpeng Pro 上手初体验

香橙派 Kunpeng Pro 上手初体验 目录 香橙派 Kunpeng Pro 上手初体验1.前言2.开箱3.开发板资源介绍硬件规格参数外观规格参数4.系统环境搭建系统镜像烧录ssh连接5.简单测试6.总结 1.前言 我很荣幸能收到了来自CSDN的测评邀请,让我有机会对香橙派最新推出的Kunpeng …

C# 使用Aspose生成和修改文档

Aspose库 C#中的Aspose库是一个强大的文件处理库,可以用于各种文件格式的创建、编辑、转换和操作。该库提供了丰富的功能,包括处理文档、电子表格、幻灯片、PDF、图像等多种文件格式,能够轻松实现文件的读取、写入、格式化、样式设置、数据操…

历时3周圆满结营,Sui Move HackerHouse精彩回顾

Sui 是基于第一原理重新设计和构建而成的 L1 公有链,旨在为创作者和开发者提供能够承载 Web3 中下一个十亿用户的开发平台。Sui 上的应用基于 Move 智能合约语言,并具有横向可扩展性,让开发者能够快速且低成本支持广泛的应用开发。 Move 是用…

Vectorworks 2024 Mac安装包下载Vectorworks 2024安装教程3D建模设计工具

安装 步骤 1,双击下载好的安装包,打开。 2,将G1DXHL.ldf拖到桌面上备用。 3,返回打开的镜像 选择install vectorworks2024 双击打开启动安装程序。电脑就90hi高腰腿疼痛和Y&Aaa9yY 4,输入电脑密码。 5&#xff0…

python onnx 推理yolov10

python onnx 推理yolov10 import onnxruntime as ort import cv2 import numpy as np# Class names for the COCO dataset CLASSES = ["person", "bicycle", "car", "motorcycle", "airplane"

DasViewer V3.3全新升级 | 新增点云数据浏览、点云关联全景照片、3DTiles在线数据浏览...

近日,DasViewer迎来3.3版本更新,新增点云数据浏览展示、点云关联全景照片、3DTiles在线数据浏览等功能,为用户带来多源数据可视化极速浏览全新体验。 一起来先睹为快↓ 新增功能 1,点云数据浏览 在新版本中,DasView…

CentOS Samba配置挂载

目录 安装samba 配置 /home 文件夹权限 配置 /home 文件夹共享 配置防火墙 添加 root 登录用户并创建密码 重启samba服务,关闭selinux 查看samba状态 以/home目录挂载为例: 安装samba sudo yum install samba 配置 /home 文件夹权限 sudo se…

rapidssl泛域名https600元一年

泛域名https证书也可以称之为通配符https证书,指的是可以用一张https证书为多个网站(主域名以及主域名下的所有子域名网站)传输数据加密,并且提供身份认证服务的数字证书产品。RapidSSL旗下的泛域名https证书性价比高,申请速度快,…

自己手写一个线性表List【C风格】

#include <iostream>//线性表、顺序表List#define MAX_SIZE 20 #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0typedef int Status;//返回状态类型 typedef int ElemType;//元素类型//结构体 typedef struct {ElemType data[MAX_SIZE];//数据类型&#x…

Python爬虫项目实战:百度任意图片抓取

大家好&#xff0c;我是南枫&#xff0c;这篇文章我将给大家介绍如何使用Python爬虫来达到想爬哪个明星图片就能爬下来的效果&#xff0c;那我们接下来看看如何实现的吧。 导入Python的requests库和re库。requests库用于发送HTTP请求&#xff0c;而re库用于处理正则表达式。 通…

【EI会议】2024年互联网技术与环境工程国际会议(IACITEE 2024)

【EI会议】2024年互联网技术与环境工程国际会议&#xff08;IACITEE 2024&#xff09; 2024 International Conference on Internet Technology and Environmental Engineering 互联网技术与环境工程国际会议&#xff08;IACITEE 2024&#xff09;将在重庆举行&#xff0c;主…

DataGrip测试连接时出现报错解决方案

&#xff08;一&#xff09;报错情况描述&#xff1a; DBMS: MySQL (无版本) 区分大小写: 普通形式mixed&#xff0c;分隔形式exact Connection refused: connect. &#xff08;二&#xff09;解决方案&#xff1a; 1、 首先打开命令指示符&#xff0c;选择以管理员身份运行。…

【vue-5】双向数据绑定v-model及修饰符

单向数据绑定&#xff1a;当数据发生改变时&#xff0c;视图会自动更新&#xff0c;但当用户手动更改input的值&#xff0c;数据不会自动更新&#xff1b; 双向数据绑定&#xff1a;当数据发生改变时&#xff0c;视图会自动更新&#xff0c;但当用户手动更改input的值&#xf…

Vue基础(数据绑定、export使用)

1、简介 在使用vue开发的过程中&#xff0c;经常会遇到一些容易混淆的问题&#xff0c;因此&#xff0c;在本文中进行汇总操作&#xff0c;只有通过不断总结学习&#xff0c;才能更好掌握vue的使用&#xff08;每天进步一点&#xff09;。 2、数据绑定 在js中定义数据&#xf…

音乐编曲软件哪个好用 studio one和fl studio哪个好

编曲软件的出现&#xff0c;打破了时间与空间的限制&#xff0c;使得创作者能随时随地进行音乐创作。随着信息时代的发展&#xff0c;使用编曲软件进行音乐创作已经成为业界主流。业内常用的有Cubsae、LogicPro、Studio One、Ableton live等&#xff0c;这次教程我将为大家解读…

HTTP 协议的基本格式和Fidder的简单使用

HTTP协议诞生于1996&#xff08;开玩笑哈&#xff0c;诞生于1991年&#xff09;&#xff0c;http协议用于网页和手机app和服务器交互的场景。通过HTTP协议&#xff0c;客户端&#xff08;例如网页浏览器或手机应用&#xff09;可以向服务器发送请求&#xff0c;服务器则会响应这…