SpringAOP专栏二《原理篇》
上一篇SpringAOP专栏一《使用教程篇》-CSDN博客介绍了SpringAop如何使用,这一篇文章就会介绍Spring AOP 的底层实现原理,并通过源代码解析来详细阐述其实现过程。
前言
Spring AOP 的实现原理是基于动态代理和字节码操作的。不了解动态代理和字节码操作的读者可以先看一下这篇文章java中的反射和代理模式-CSDN博客
实现原理
下面我会基于在使用SpringAOP进行逻辑增强时各个核心类的执行顺序进行底层原理的剖析
实现代理和切面逻辑的核心类执行顺序如下:
1.Bean 实例化阶段:
- BeanPostProcessor:在 Bean 实例化之后,进行初始化前的处理。其中,AbstractAutoProxyCreator 是一个重要的 BeanPostProcessor 实现类,用于自动创建代理对象。
2.切面织入阶段:
- AdvisedSupport:封装了切面逻辑和目标对象信息。
- ProxyFactoryBean:生成代理对象的工厂类。
- AopProxyFactory:AOP 代理对象的工厂接口。
- AopProxy:AOP 代理对象的核心接口,定义了获取代理对象的方法。
- CglibAopProxy:基于 CGLIB 的动态代理实现类。
- DynamicAdvisedInterceptor:CGLIB 动态代理的回调函数实现类,用于触发切面逻辑的执行。
3.方法拦截与执行阶段:
- ProxyMethodInvocation:代理方法调用的核心类,封装了目标对象方法和参数信息。
- ReflectiveMethodInvocation:反射调用目标方法的类,是 ProxyMethodInvocation 的具体实现类。
- MethodInvocationInterceptor:方法拦截器接口,定义了拦截器的方法执行逻辑。
- MethodInterceptor:CGLIB 库中的接口,被 MethodInvocationInterceptor 实现。
Bean 实例化阶段
在service bean的创建过程中(也就是getBean("service")),AOP通过BeanPostProcess后置处理器操作进行介入 分为2种情况:
- 用户自定义了
targetSource,则bean的创建(实例化、填充、初始化)均由用户负责,Spring Ioc不会在管该代理目标对象traget,这种情况基本上不会发生,很多人用了几年Spring可能都不知道有它的存在- 正常情况下都是Spring Ioc完成代理对象target的实例化、填充、初始化。然后在初始化后置处理器中进行介入,对bean也就是service进行代理
下面是Bean示例化的流程图

切面织入阶段
Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的支持局限于方法级别的拦截。
在这个阶段会读取相关的SpringAOP的配置,如何将切面逻辑和目标对象信息封装到AdvisedSupport中,再通过ProxyFactoryBean(生成代理对象的工厂类)根据目标对象是否实现接口来调用JDK动态代理还是Cglib代理。
下面详细介绍一下两者代理方式的实现源码:
Spring AOP 动态代理实现:
- 默认情况下,实现了接⼝的类,使⽤ AOP 会基于 JDK ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类。
- JDK Proxy(JDK 动态代理)
- CGLIB Proxy:默认情况下 Spring AOP 都会采用 CGLIB 来实现动态代理,因为效率高
- CGLIB 实现原理:通过继承代理对象来实现动态代理的(子类拥有父类的所有功能)
- CGLIB 缺点:不能代理最终类(也就是被 final 修饰的类)
JDK动态代理
JDK 动态代理是 Java 自带的动态代理实现方式。使用JDK动态代理时,需要目标对象实现至少一个接口。JDK 动态代理会在运行时生成一个实现了目标对象接口的代理类,该代理类会在目标对象方法执行前后插入切面代码。
下面是JdkDynamicAopProxy类的部分源码,感兴趣的读者可以自己去看看,我这里就不一一截图给大家看了。

如果读者看源码如果有困难,可以看一下这个我简化了的JdkDynamicAopProxy类。这个类我保留了主要逻辑,把一些提高代码健壮性的部分去掉了。
JdkDynamicAopProxy 类的主要作用是将切面逻辑织入到目标对象的方法调用中。当使用基于接口的代理方式时,Spring AOP 使用 JDK 动态代理来创建代理对象。JdkDynamicAopProxy 类会根据配置的切面信息,动态地生成一个代理对象,并将切面逻辑织入到该代理对象的方法调用中。
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {private final AdvisedSupport advised;public JdkDynamicAopProxy(AdvisedSupport advised) {this.advised = advised;}@Overridepublic Object getProxy() {return Proxy.newProxyInstance(getClass().getClassLoader(),advised.getTargetSource().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInterceptor methodInterceptor = advised.getMethodInterceptor();MethodInvocation methodInvocation = new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(),method,args,methodInterceptor,advised.getTargetSource().getTargetClass());return methodInvocation.proceed();}
}
在该代码中,JdkDynamicAopProxy 类实现了 AopProxy 和 InvocationHandler 接口。

AopProxy 接口是 Spring AOP 提供的代理接口,它定义了获取代理对象的方法。JdkDynamicAopProxy 类通过实现该接口,提供了基于 JDK 动态代理的代理对象获取功能。

InvocationHandler 接口是 JDK 提供的反射 API 中的一部分,它定义了一个 invoke 方法,用于在代理对象上调用被代理方法。JdkDynamicAopProxy 类实现了 InvocationHandler 接口,通过重写 invoke 方法,实现对被代理方法的增强逻辑。
下面是对简化了的JdkDynamicAopProxy类的详细解读
AdvisedSupport类型的属性advised:表示该代理对象所依赖的AdvisedSupport对象,该对象包含了切面配置信息。构造函数
JdkDynamicAopProxy(AdvisedSupport advised):接收一个AdvisedSupport对象作为参数,并将其赋值给advised属性。
getProxy()方法:实现了AopProxy接口中的方法,用于获取代理对象。它通过调用Proxy.newProxyInstance()方法创建代理对象。
getClass().getClassLoader()获取当前类的类加载器。advised.getTargetSource().getInterfaces()获取目标对象实现的接口数组。this表示使用当前对象作为代理对象的InvocationHandler。
invoke(Object proxy, Method method, Object[] args)方法:实现了InvocationHandler接口中的方法,拦截目标对象方法的调用并进行增强逻辑。
- 首先,从
advised对象中获取MethodInterceptor实例,即切面逻辑。- 然后,创建一个
ReflectiveMethodInvocation实例,传入目标对象、目标方法、方法参数、切面逻辑和目标对象的类信息。- 最后,调用
methodInvocation.proceed()方法执行切面逻辑,并返回方法的执行结果。
CGLIB 代理
CGLIB 代理是一个基于字节码操作的代理方式,它可以为没有实现接口的类创建代理对象。CGLIB 代理会在运行时生成一个目标对象的子类,并覆盖其中的方法,以实现AOP的功能。
下面是CglibAopProxy类的部分源代码,感兴趣的读者可以自己去看看,我这里就不一一截图给大家看了。


如果读者看源码如果有困难,可以看一下这个我简化了的CglibAopProxy类。这个类我保留了主要逻辑,对代码进行了简化。
public class CglibAopProxy implements AopProxy {private final AdvisedSupport advised;public CglibAopProxy(AdvisedSupport advised) {this.advised = advised;}@Overridepublic Object getProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(advised.getTargetSource().getTargetClass());enhancer.setCallback(new DynamicAdvisedInterceptor(advised));return enhancer.create();}private static class DynamicAdvisedInterceptor implements MethodInterceptor {private final AdvisedSupport advised;public DynamicAdvisedInterceptor(AdvisedSupport advised) {this.advised = advised;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {MethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(),method,args,proxy,advised.getMethodInterceptor(),advised.getTargetSource().getTargetClass());return methodInvocation.proceed();}}
}
CglibAopProxy 类主要有以下作用
生成代理对象:CglibAopProxy 根据目标对象和切面逻辑生成一个代理对象。与 JDK 动态代理不同,CGLIB 代理不需要目标对象实现接口。
增强方法逻辑:通过继承目标对象,CglibAopProxy 重写目标对象的方法,并在方法中添加切面逻辑。这样,在调用代理对象的方法时,会先执行切面逻辑,然后再调用目标对象的原始方法。
拦截方法调用:CglibAopProxy 使用 MethodInterceptor 接口来定义切面逻辑,并将其应用于生成的代理对象。在代理对象的方法调用过程中,切面逻辑会被触发,从而实现横切关注点的功能,如事务管理、日志记录等。
高性能的代理:相较于 JDK 动态代理,CGLIB 代理使用了字节码生成技术,生成的代理对象是目标对象的子类。这种方式避免了通过反射调用目标对象的方法,提供了更高的执行性能。
下面是CglibAopProxy类的详细解读
CglibAopProxy 类实现了 AopProxy 接口,该接口定义了获取代理对象的方法 getProxy()。
CglibAopProxy 类的构造方法需要一个 AdvisedSupport 对象作为参数,AdvisedSupport 是 Spring AOP 框架中的核心类,用于保存切面逻辑和目标对象信息。
getProxy() 方法中,首先实例化了 Enhancer 对象,Enhancer 是 CGLIB 库中的主要类,用于生成代理对象。
setSuperclass() 方法将目标对象的类设置为要生成的代理类的父类,这样代理类就可以继承目标类的所有非私有方法。
setCallback() 方法用于设置代理类的回调函数,即在代理类的方法调用时,会触发回调函数中的逻辑。
DynamicAdvisedInterceptor 类实现了 MethodInterceptor 接口,这个接口是 CGLIB 库中的接口,用于定义代理类的回调函数。
intercept() 方法是 DynamicAdvisedInterceptor 类的核心方法。当代理类的方法被调用时,intercept() 方法会被触发。在该方法中,首先将目标对象的方法和参数封装成 MethodInvocation 对象,然后调用其 proceed() 方法,从而触发切面逻辑的执行。
CglibMethodInvocation 类是 MethodInvocation 接口的实现类,用于封装目标对象的方法和参数信息。
方法拦截与执行阶段
该阶段的实际执行流程为:
当代理对象的方法被调用时,会创建一个 ProxyMethodInvocation 对象,并将目标对象、目标方法、方法参数等信息传递给它。
ProxyMethodInvocation 继承了 ReflectiveMethodInvocation 类,因此它可以通过反射调用目标方法。
在拦截器链的创建过程中,AdvisorChainFactory 会根据切点和通知创建 Advisor 链,即将所有与目标方法匹配的切面的方法拦截器添加到拦截器链中。
当 ProxyMethodInvocation 执行目标方法时,它会依次遍历拦截器链中的每个方法拦截器。
每个方法拦截器在方法调用前后执行自己的逻辑,可以实现前置通知、后置通知、异常处理和返回通知等功能。
方法拦截器的执行顺序与它们在拦截器链中的顺序一致。在方法调用之前,拦截器依次执行前置通知;在方法调用之后,拦截器依次执行后置通知;如果方法发生异常,拦截器执行异常处理逻辑;最后,拦截器执行返回通知。
下面我来分析一下SpringAOP的拦截器
SpringAOP的拦截器
我们先来了解一下拦截器的执行属性:如下图所示

接下来看一下AOP拦截器执行原理,拦截器是如何保证不同通知注解下的方法的执行顺序的呢?
在 Spring AOP 中,拦截器链的执行顺序是由 AdvisedSupport 类中的方法获取的。AdvisedSupport 包含了代理对象需要的所有信息,包括目标对象、代理接口、拦截器等。其中,拦截器链的创建和执行是在 ExposeInvocationInterceptor 这个拦截器中完成的。
在创建拦截器链时,AdvisorChainFactory 会根据切面的顺序将各个切面的拦截器顺序组合成一个拦截器链。
这样就可以保证 before 在 after 之前执行,因为在创建拦截器链的过程中,会按照切面的顺序将各个切面的拦截器依次添加到链中,最终形成一个有序的拦截器链。
另外,对于同一个方法的多个切面,Spring AOP 会根据切面的顺序将它们的拦截器依次添加到拦截器链中,从而保证了它们的执行顺序。这样就可以保证 find 方法的执行顺序符合切面的定义顺序。
因此,通过 Spring AOP 框架内部对拦截器链的创建和执行机制的设计,可以保证拦截器的执行顺序满足业务需求,确保了 before 在 after 之前执行,并且保证了多个拦截器的执行顺序。
相关文章:
SpringAOP专栏二《原理篇》
上一篇SpringAOP专栏一《使用教程篇》-CSDN博客介绍了SpringAop如何使用,这一篇文章就会介绍Spring AOP 的底层实现原理,并通过源代码解析来详细阐述其实现过程。 前言 Spring AOP 的实现原理是基于动态代理和字节码操作的。不了解动态代理和字节码操作…...
冒泡排序(函数)
冒泡排序,将一个列表中的两个元素进行比较,并将最小的元素交换到顶部。两个元素中较小的会冒到顶部,而较大的会沉到底部,该过程将被重复执行,直到所有元素都被排序。 输入格式: 输入在第1行中给出N(1<N…...
Vue3中的defineModel
目录 一、vue3的defineModel介绍 二、defineModel使用 (1)在vite.config.js中开启 (2)子组件 (3)父组件 一、vue3的defineModel介绍 为什么要使用到defineModel呢?这里有这样一种场景&…...
动态内存管理(C语言)
前言 在学习数据结构时,掌握指针、结构体和动态内存管理是非常关键的,它们就像是搭建程序框架和操作内存的工具箱,需要熟练掌握才能更加游刃有余地进行编程。 指针和结构体我们已经在之前详细的讲过了,今天我将带大家学习动态内存…...
区块链实验室(32) - 下载arm64的Prysm
Prysm是Ethereum的共识层。 1. 下载prysm.sh curl https://raw.githubusercontent.com/prysmaticlabs/prysm/master/prysm.sh --output prysm.sh && chmod x prysm.sh2. 下载x86版prysm共识客户端 ./prysm.sh beacon-chain --download-only3.下载arm64版prysm共识客…...
flutter学习-day3-dart基础
📚 目录 变量声明操作符数据类型控制流错误处理和捕获函数mixin异步 FutureStream 本文学习和引用自《Flutter实战第二版》:作者:杜文 1. 变量声明 var 类似于 JavaScript 中的var,它可以接收任何类型的变量,但最大…...
gitblit自建git仓库
在Ubuntu服务器 安装 java sudo apt-get update sudo apt-get install openjdk-8-jdk # 或者其它你喜欢的版本//sudo snap install gitblit 验证: java -version下载 gitblit https://github.com/gitblit-org/gitblit/releases解压/usr/local tar -zxvf gitb…...
二百一十一、Flume——Flume实时采集Linux中的Hive日志写入到HDFS中(亲测、附截图)
一、目的 为了实现用Flume实时采集Hive的操作日志到HDFS中,于是进行了一场实验 二、前期准备 (一)安装好Hadoop、Hive、Flume等工具 (二)查看Hive的日志在Linux系统中的文件路径 [roothurys23 conf]# find / -name…...
python 实现 AIGC 大模型中的概率论:生日问题的基本推导
在上一节中,我们对生日问题进行了严谨的阐述:假设屋子里面每个人的生日相互独立,而且等可能的出现在一年 365 天中的任何一天,试问我们需要多少人才能让某两个人的生日在同一天的概率超过 50%。 处理抽象逻辑问题的一个入手点就是…...
YOLOv8算法改进【NO.87】引入上下文引导网络(CGNet)的Light-weight Context Guided改进C2_f
前 言 YOLO算法改进系列出到这,很多朋友问改进如何选择是最佳的,下面我就根据个人多年的写作发文章以及指导发文章的经验来看,按照优先顺序进行排序讲解YOLO算法改进方法的顺序选择。具体有需求的同学可以私信我沟通: 第一,创新主干特征提取网络,将整个Backbone改…...
GPT-4V 在机器人领域的应用
在科技的浩渺宇宙中,OpenAI如一颗璀璨的星辰,于2023年9月25日,以一种全新的方式,向世界揭示了其最新的人工智能力作——GPT-4V模型。这次升级,为其旗下的聊天机器人ChatGPT装配了语音和图像的新功能,使得用…...
Java基础语法之访问修饰限定符
private 表示私有的,只能在同一个包中的同一个类使用 像这样就是在同一个包中的不同类用了private修饰的变量,这是非法的,那到底该如何给a赋值呢?可以在定义时就赋值,但这样的代码就没有可操作性,所以我们…...
算法通关村第十八关 | 青铜 | 回溯
1.回溯 回溯可以视为递归的拓展,有着明确的解题模板。 很大的不同之处是有一个撤销处理结果的操作,但是大框架就是遍历 N 叉树。 回溯主要解决暴力枚举都解决不了的问题。 回溯模板: void backtracking(参数) {if (终止条件) {存放结果;…...
蓝牙在物联网中的应用,相比WIFI和NFC的优势?
蓝牙在物联网中有着广泛的应用,主要包括以下几个方面: 1、智能家居:蓝牙Mesh技术可以用于智能家居设备之间的连接和通信,实现设备的远程控制和管理。例如,通过蓝牙技术可以将智能音箱、智能电视、智能家电等设备连接起…...
Altair推出 Altair RapidMiner 2023 平台,提供生成式 AI 功能
Altair推出 Altair RapidMiner 2023 平台,提供生成式 AI 功能 更新包括自动聚类、扩展 SAS、Python 和 R 编程功能等 近日,Altair(纳斯达克股票代码:ALTR)近日宣布其数据分析和 AI 平台 Altair RapidMiner 取得了一系…...
包管理工具npm与yarn
1.npm 1.1 安装 安装node后自带了npm 2.2 初始化package.json npm init 1.3 安装包 单个包:npm install less或npm i less 所有包:npm installnpm i 1.4 删除包 npm remove less,npm r less或npm uninstall less 1.5 配置别名 pack…...
深度学习 Day11——T11优化器对比实验
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 | 接辅导、项目定制 文章目录 前言一、我的环境二、代码实现与执行结果1.引入库2.设置GPU(如果使用的是CPU可以忽略这步)3.导入数据4.查…...
(十六)Flask之蓝图
蓝图 Flask蓝图(Blueprint)是Flask框架中用于组织和管理路由、视图函数以及静态文件的一种机制。它提供了一种将应用程序拆分为更小、可重用组件的方式,使得项目结构更清晰,代码更易于维护。 使用Flask蓝图,可以将相…...
面试问题--文件IO
文件 I/O 操作在 C 语言中的使用 在 C 语言中,文件 I/O(Input/Output)操作是处理文件的重要部分。本文将介绍一些常见的文件 I/O 操作及其使用示例。 打开和关闭文件 1.打开文件: fopen() 函数用于打开一个文件。 FILE *fpt…...
SpringBoot中实现跨域的几种常用方式
在SpringBoot中实现跨域请求可以通过以下几种方式: 1. 使用CrossOrigin注解,可以直接在Controller层的方法上使用,用来指定允许跨域请求的来源、方法和头信息。例如: CrossOrigin(origins "http://localhost:8080") …...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...



