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") …...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...



