【Spring】 AOP 核心原理,与声明式事务传播机制

news2026/5/21 21:02:56
一、什么是 AOPAOPAspect Oriented Programming面向切面编程核心思想在不修改原有业务代码的情况下对方法进行统一增强。例如日志记录权限校验事务管理性能统计异常处理这些都属于“公共功能”不应该和业务代码耦合在一起。为什么需要 AOP传统开发public void save(){// 日志// 权限校验// 事务// 业务代码}问题代码重复耦合度高不方便维护修改公共逻辑需要改很多地方AOP 的做法把公共逻辑抽取出来在方法执行前后动态置入。二、AOP 的核心概念1.五个概念术语含义用人话解释Joinpoint (连接点)程序中可植入增强的代码点具体来说就是系统中的各个方法。PointCut (切入点)真正决定要被拦截、增强的方法集合也就是我们定义的拦截规则如某个包下的所有方法。Advice (通知/增强)切面主要做的事情是具体的业务代码比如“在方法前记日志”、“在方法后提交事务”。Aspect (切面)切入点 通知 的集合一个切面定义了在哪里PointCut做什么Advice。Proxy (代理)Spring AOP 底层通过代理实现真正执行的是代理对象不是原对象代理对象包装了原有业务对象并织入了增强逻辑。2.通知类型1. Before()前置通知方法执行前执行如权限校验参数校验2. After 后置通知方法执行后执行无论是否异常都会执行类似finally3. AfterReturning 返回通知方法正常返回后执行4. AfterThrowing 异常通知方法发生异常时执行5. Around 环绕通知最重要可以方法前增强方法后增强控制方法是否执行修改返回值统计耗时3.Around 环绕通知举例// Aspect 表明这是一个切面类 // Order(1) 有多个切面时数字越小优先级越高最先被执行 Aspect Order(1) Component // 必须交给 Spring 容器管理 public class TimeAspect { Around(⭐execution(* com.demo.controller.*.*(..))) public ⭐Object around(ProceedingJoinPoint pjp) ⭐throws Throwable { // 1. 方法执行前记录开始时间 long start System.currentTimeMillis(); // 2. 执行目标方法 ⭐Object result pjp.proceed(); // 3. 方法执行后记录结束时间 long end System.currentTimeMillis(); System.out.println(耗时 (end - start)); return result; } }⭐execution 切点表达式execution(访问修饰符 返回值 包名.类名.方法名(参数))*匹配任意字符任意返回值service 包下任意类任意方法任意参数..任意层级例如com.demo..⭐Around 返回 Object因为要兼容所有方法返回值除了Around类型外其他通知Before, After 等定义时建议设为void不写返回值即使写了Spring 也会忽略不会被作为业务返回值传给调用方⭐pjp.proceed()必须手动用throws Throwable抛出不能在这里try-catch将异常截获否则原本的异常处理逻辑如AfterThrowing或统一异常处理将失效⭐ProceedingJoinPointpjp.proceed()作用是执行目标方法除环绕通知外的其他通知类型会自动执行目标方法但是环绕通知必须手动执行♦ Around 做什么了记录耗时♦Pointcut切点复用//定义切点 Pointcut(execution(* com.demo.service.*.*(..))) public void pt(){} //使用切点 Before(pt())4.AOP 执行流程Spring 启动时扫描 Bean找到带有Aspect的切面类找到切点表达式判断哪些 Bean 需要增强为这些 Bean 创建代理对象调用方法时执行增强逻辑5.Spring AOP 底层原理代理对象是在程序运行期间动态生成的将方法调用从客户端拦截下来由代理对象执行一些额外的逻辑增强然后再选择是否调用目标对象Target的原始方法静态代理vs 动态代理静态代理手动为每个目标类编写一个代理类编译为.class文件。在代码运行前就已经确定。缺点随着业务类增加代理类数量暴增难以维护。动态代理Spring AOP 采用在程序运行期间由框架在内存中动态生成代理对象。不需要手动编写.class文件。动态代理动态代理先生成“类字节码”再根据这个类去创建“Bean 实例”1JDK 动态代理目标类必须实现类接口代理类动态创建一个实现相同接口的类2CGLIB 动态代理通过继承目标类生成子类代理不需要接口Spring 默认优先使用 JDK没有接口时使用 CGLIBSpring 动态代理的决策逻辑常问如果目标对象实现了接口默认使用JDK 动态代理。如果目标对象未实现接口则必须使用CGLIB 动态代理。【注】Spring Boot 2.x 后默认配置可能偏向全员使用 CGLIB 以避免因为是否实现接口导致的行为不一致。派系实现机制优缺点/适用场景JDK 动态代理基于接口实现。代理类与目标类实现相同的接口。通过InvocationHandler和Proxy类实现。优点速度快。缺点只能代理实现了接口的类。若无接口无法使用。CGLIB 动态代理基于继承实现。生成的代理类是目标类的子类。使用底层字节码技术生成。优点可以代理没有接口的类。性能在某些场景下优于 JDK。缺点不能代理final方法。final类不能被继承三、Spring 事务事务是保证数据完整性和一致性的核心手段。在 Spring 中我们通过简单的一个Transactional注解即可实现这一复杂功能其底层同样是基于AOP 代理机制1. 事务的核心概念Transaction定义将一组逻辑上相关的操作包装成一个原子单位这些操作要么全部成功要么全部失败。底层支撑Spring 本身并不管理事务而是提供了一套一致的接口具体实现依赖底层的数据库如 MySQL 的 InnoDB 引擎支持事务MyISAM 不支持。2. 声明式事务 vs 编程式事务编程式事务在业务代码中手动写commit()、rollback()逻辑。缺点侵入性强难以维护。声明式事务AOP 代理形式在方法或类上加上Transactional注解。底层原理Spring 产生目标类的代理对象。当调用方法时代理对象先开启事务然后执行业务逻辑如果没有发生异常则代理提交事务如果发生特定异常则代理回滚事务。3. Transactional 的核心配置要点作用域可以作用在方法上也可以作用在类上类下所有方法都生效。底层 AOP 切点实现思路切点所有加了Transactional的方法。通知逻辑Advice代理开启事务TransactionStatus状态 - 执行原业务 - 成功则代理提交 / 失败则代理回滚。4. 事务回滚规则设置默认回滚异常Spring 只对RuntimeException运行时异常和Error进行回滚。注意IOException检查异常、FileNotFoundException等默认是不回滚的必须手动配置。解决方案Transactional(rollbackFor Exception.class)推荐全员配置拦截所有类型的异常。或者手动try-catch异常并在代码中调用底层 API 手动回滚但这就变成了编程式事务。5. 事务传播属性Propagation - 面试必问当一个事务方法调用另一个事务方法时事务应该如何处理propagation Propagation.REQUIRED(默认值)外层方法有事务就加入如果没有外层方法自己新建一个。propagation Propagation.REQUIRES_NEW无论外层有没有事务自己都必须新建一个事务并且把外层事务挂起如果是外层事务调入两个事务互不影响外层异常回滚不影响子事务。【注】这种策略在处理写独立审计日志、发放奖品等逻辑时非常常用。四、Spring 事务传播机制为了方便理解我们统一设定一个代码调用背景方法 A开启了事务或没有。方法 A 内部调用了方法 B方法 B 配置了不同的传播机制传播行为 (Propagation)外层A有事务时外层A无事务时说明REQUIRED(默认)加入A事务新建事务融合在一起同生共死REQUIRES_NEW挂起A新建事务新建事务彼此独立互不干扰NESTED嵌套子事务(Savepoint)新建事务A崩全崩B崩A可不崩SUPPORTS加入A事务非事务运行随遇而安NOT_SUPPORTED挂起A非事务运行非事务运行拒绝事务强制裸奔MANDATORY加入A事务抛出异常必须有靠山没靠山就罢工NEVER抛出异常非事务运行见事务就见光死同类方法调用失效问题如果方法 A 和方法 B 在同一个 Service 类里方法 AREQUIRED调用方法 BREQUIRES_NEWB 的新事务会生效吗答案不会生效因为 Spring 事务基于 AOP 动态代理。类内部的直接方法调用this.B()指向的是真实目标对象而不是外面的代理对象导致 B 的Transactional注解直接被忽略B 会直接沿用 A 的事务等同于 REQUIRED。根本原因this.B()是一次普通的 JVM 内部方法调用根本没有走 Spring 代理对象的业务分发链路。既然没经过代理加在B()上的所有 AOP 注解事务、日志、权限自然全部失效。流程:外部调用如 Controller 调用userService.A()---------调用首先走到UserService代理对象---------代理对象触发开启事务---------代理对象内部调用target.A()---------A()方法内正在执行的是目标对象的代码---------A调用this.B---------调用的是真实目标对象Service public class UserService { Transactional public void A() { // 外部调用进来事务成功开启 // 此时 code 在 Target 对象内部运行 this.B(); // 相当于 target.B(); } Transactional(propagation Propagation.REQUIRES_NEW) public void B() { // 因为是 target 直接调用的没有经过外面的 Proxy // 代理对象身上的 AOP 增强新开事务直接被绕过了 } }解决办法使用AopContext.currentProxy()获取当前代理对象去调用 B或者将 B 拆分到不同的 Service 类中。①核心大三策略最常用这是笔记中重点标记、面试出镜率高达 90% 的三种机制。1. REQUIRED必须有事务—— 默认策略人话解释有同享无自建。业务逻辑若 A有事务B 就会加入A 的事务变成同一个事务。若 A没有事务B 就会自己新建一个事务。致命痛点由于 A 和 B 在同一个事务中其中任何一处报错导致回滚全盘回滚即便 B 报错被 A 用try-catch捕获了也会因为全局事务被标记为rollback-only而引发异常回滚。2. REQUIRES_NEW必须新事务人话解释不管你有没有我都要住单间。业务逻辑不管 A 有没有事务B 都会开启一个全新的事务。若 A有事务A 会先挂起Suspend等待 B 的事务运行完A 再继续运行。影响结果A 和 B 是两个独立的事务。B 报错回滚不会影响 A 的正常提交前提是 A 捕获了 B 的异常同理A 后面报错回滚也绝对不会影响已经提交的 B 事务。典型场景记录审计日志、发放独立优惠券。无论核心下单业务成功与否日志必须入库。3. NESTED嵌套事务业务逻辑若 A有事务B 会在 A 的事务内部设立一个保存点Savepoint。关键特质局部回滚如果 B 报错回滚B 只会回滚到 Savepoint不影响 A 事务的执行A 捕获异常后可以继续提交。全局连坐如果 A 报错回滚由于 B 是嵌套在 A 里面的AB 会全部回滚。② 挂载与不支持策略3个不常用这三个策略通常用于优化特定场景下的性能如纯查询逻辑。4. SUPPORTS支持事务业务逻辑A有事务B 就加入A 的事务。A没有事务B 就以非事务普通方式运行。典型场景只读查询方法。5. NOT_SUPPORTED不支持事务业务逻辑始终以非事务方式运行。即使 A有事务B 也会把 A 的事务挂起等自己用非事务方式裸奔完再恢复 A 的事务。6. MANDATORY强制要求有事务业务逻辑极度傲娇。A必须有事务B 才会加入。若 A没有事务B 直接抛出异常IllegalTransactionStateException。③ 绝对禁区7. NEVER禁用事务业务逻辑始终以非事务方式运行。如果 A有事务B 逮到直接抛出异常。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…