第五篇:Spring事务管理——@Transactional的底层实现与失效场景

news2026/5/11 3:10:01
前言在前面的文章中我们拆解了Spring AOP的底层原理——动态代理和切面编程。现在我们来看AOP最经典的应用事务管理。你每天用着Transactional往Service方法上一加事务就自动开启了。但面试中事务是Spring最深的水区之一“Transactional是怎么实现的它和AOP是什么关系”“为什么同一个类中方法互相调用事务会失效”“事务传播行为有哪些REQUIRES_NEW和NESTED有什么区别”“什么情况下Transactional会失效至少说出三种场景。”这些问题考察的不是会不会加注解而是理不理解事务拦截的底层机制。本文从事务拦截链出发逐层拆解Spring事务管理的实现原理和经典失效场景。本文核心问题Transactional是怎么实现的TransactionInterceptor做了什么事务传播行为有哪些REQUIRED、REQUIRES_NEW、NESTED分别适用什么场景为什么同一个类中方法互相调用事务会失效Transactional注解在哪些场景下会失效至少说出四种失效场景及其根本原因。编程式事务和声明式事务各有什么优劣为什么Transactional是默认选择Spring事务和数据库事务是什么关系Spring在这里加了什么读完本文你将对Spring事务管理拥有从实现原理到避坑指南的完整理解。一、Transactional是怎么实现的——TransactionInterceptor疑问加上Transactional注解方法就能自动开启事务。Spring是怎么做到的回答和AOP一样——Spring为标注了Transactional的Bean创建代理对象在代理对象中由TransactionInterceptor拦截方法调用在方法前后管理事务。1.1 事务拦截的本质你写的Service Transactional public void createOrder(Order order) { orderMapper.insert(order); // ← 直接写数据库 } 实际运行时AOP代理 TransactionInterceptor.invoke(proxy, method, args): transactionManager.begin() // 开启事务 try: method.invoke(target, args) // 调用你的createOrder方法 transactionManager.commit() // 提交事务 catch: transactionManager.rollback() // 回滚事务 throw关键是TransactionInterceptor——它在代理对象中拦截了Transactional注解的方法把事务管理逻辑开启、提交、回滚包裹在业务方法前后。业务方法内部不需要写任何事务代码Spring通过AOP把事务逻辑从业务代码中完全剥离。1.2 事务拦截的触发条件Spring根据以下规则从容器管理的Bean中筛选出需要被事务拦截的类或方法类或方法上标注了Transactional如果是类级别标注则类中所有public方法都受事务管理如果是方法级别标注只有标注的方法受事务管理类中未标注的方法不受影响1.3 事务的创建过程TransactionInterceptor获取事务时核心参数包括事务传播行为、隔离级别、超时时间、只读标志和回滚规则。这些参数从Transactional注解中读取传递给事务管理器。事务管理器根据传播行为判断是新建事务还是加入已有事务然后设置隔离级别和超时控制最终返回事务状态对象。整个创建过程在TransactionInterceptor内部完成业务代码完全无感知。二、事务传播行为——REQUIRED、REQUIRES_NEW、NESTED的区别疑问事务传播行为是什么REQUIRED和REQUIRES_NEW有什么区别回答传播行为定义了一个事务方法被另一个事务方法调用时事务应该如何传播——是加入调用方的事务还是新建一个独立的事务。2.1 七种传播行为传播行为含义调用方有事务调用方无事务REQUIRED默认需要事务加入已有事务新建事务REQUIRES_NEW需要新事务挂起原事务新建独立事务新建事务NESTED嵌套事务创建保存点Savepoint新建事务SUPPORTS支持事务加入已有事务无事务执行NOT_SUPPORTED不支持事务挂起原事务无事务执行无事务执行MANDATORY强制需要事务加入已有事务抛异常NEVER不允许事务抛异常无事务执行2.2 REQUIRED vs REQUIRES_NEW场景订单创建成功时需要记录一条操作日志。创建订单失败时日志不能因为事务回滚而丢失。 REQUIRED错误方案 createOrder() { // 外层事务A orderMapper.insert(); // 写订单 logService.log(); // 写日志——使用REQUIRED加入外层事务 } 如果log()抛异常 → 整个事务A回滚 → 订单插入和日志插入都回滚 → 错误 REQUIRES_NEW正确方案 createOrder() { // 外层事务A orderMapper.insert(); // 写订单 logService.log(); // 写日志——使用REQUIRES_NEW挂起事务A新建事务B } 如果log()抛异常 → 事务B回滚事务A不受影响 → 订单创建成功日志丢失 如果createOrder()抛异常 → 事务A回滚事务B已提交 → 订单回滚日志保留REQUIRED和REQUIRES_NEW的本质区别REQUIRED让所有操作共享同一个事务同生共死REQUIRES_NEW把内层操作从外层事务中隔离出来独立提交或回滚。后者适合日志记录、通知发送等无论主流程成功与否都应记录的辅助操作。2.3 REQUIRES_NEW vs NESTEDNESTED和REQUIRES_NEW都会开启新事务但隔离方式不同NESTED在已有事务中使用保存点Savepoint内层回滚时可以回滚到保存点而不影响外层事务REQUIRES_NEW则是完全独立的新事务直接挂起外层事务。NESTED依赖JDBC的Savepoint机制仅支持JDBC且需要特定数据库支持如MySQL的InnoDB引擎。REQUIRES_NEW没有这个限制任何支持事务的环境都能使用但会占用额外的数据库连接。三、Transactional失效的四种经典场景疑问明明加了Transactional为什么事务没有生效回答Transactional依赖AOP代理来实现所有绕过代理的调用都会导致事务失效。3.1 失效场景一自调用——同一个类中方法互相调用ServicepublicclassOrderService{TransactionalpublicvoidcreateOrder(Orderorder){orderMapper.insert(order);// 有事务}publicvoidbatchCreate(ListOrderorders){for(Orderorder:orders){this.createOrder(order);// ← 事务失效}}}为什么失效this.createOrder()调用的是原始对象的方法this指针完全绕过了Spring生成的代理对象。事务拦截器在代理对象中生效而this引用指向的是原始对象——事务拦截器根本没有机会拦截这次调用。batchCreate本身没有事务它通过this调用的createOrder也不会有事务。解决方案将createOrder方法移到另一个Service中从容器中注入那个Service后调用或者通过AopContext.currentProxy()获取当前类的代理对象再调用自己的方法。3.2 失效场景二非public方法ServicepublicclassOrderService{TransactionalprivatevoidcreateOrderInternal(Orderorder){// ← private方法事务失效orderMapper.insert(order);}}为什么失效Spring AOP默认使用CGLIB动态代理——CGLIB通过继承目标类生成子类在子类中重写父类方法并插入拦截逻辑。private方法无法被继承、无法被重写——CGLIB对它无能为力。同理final方法也无法被重写事务同样不会生效。3.3 失效场景三异常类型不匹配TransactionalpublicvoidcreateOrder(Orderorder){try{orderMapper.insert(order);}catch(Exceptione){log.error(创建订单失败,e);// 吞掉异常不抛出}}Spring默认只在遇到RuntimeException和Error时回滚事务。CheckedException如IOException、SQLException默认不回滚除非通过Transactional(rollbackFor Exception.class)显式指定。而这里异常被try-catch吞掉后不仅默认的回滚条件没有触发事务拦截器也无从得知异常发生——它看到方法正常返回了直接提交了事务。3.4 失效场景四数据库引擎不支持事务CREATETABLEtb_order(idBIGINTPRIMARYKEY,...)ENGINEMyISAM;-- MyISAM不支持事务MyISAM引擎不支持事务——Transactional加了也没用因为底层数据库根本不支持事务的ACID特性。Spring事务管理依赖数据库的事务能力引擎不支持则Spring的一切事务逻辑都是空转。确保使用InnoDB引擎MySQL 5.5默认。3.5 失效场景速查表失效场景根本原因解决方案自调用this调用绕过了AOP代理抽到另一个Service中或通过AopContext.currentProxy()获取代理对象非public方法CGLIB无法重写私有/受保护方法改为public方法异常被吞掉事务拦截器没有感知到异常发生抛出异常让拦截器捕获或用TransactionAspectSupport手动回滚异常类型不匹配默认只对RuntimeException和Error触发回滚Transactional(rollbackFor Exception.class)数据库引擎不支持底层根本不支持事务使用InnoDB引擎四、编程式事务 vs 声明式事务疑问既然Transactional这么好用为什么还需要编程式事务回答Transactional适合大多数场景——事务范围和方法边界一致。但当需要在方法内部手动控制事务边界时编程式事务更灵活。4.1 声明式事务TransactionalTransactionalpublicvoidprocessOrder(Orderorder){// 整个方法是一个事务同生共死orderMapper.insert(order);inventoryService.deduct(order);paymentService.pay(order);}优点简洁事务和业务代码彻底分离。缺点事务范围固定整个方法无法在方法内灵活调整。4.2 编程式事务TransactionTemplatepublicvoidprocessOrder(Orderorder){// 第一步创建订单必须成功transactionTemplate.execute(status-{orderMapper.insert(order);returnnull;});// 第二步扣库存独立事务失败不影响订单创建try{transactionTemplate.execute(status-{inventoryService.deduct(order);returnnull;});}catch(Exceptione){log.error(扣库存失败但订单已创建,e);}// 第三步发送通知异步不需要事务notificationService.send(order);}优点事务边界灵活可在方法内精细控制。缺点事务代码和业务代码混在一起不如Transactional简洁。4.3 什么时候用编程式事务方法内需要多个不同传播行为的事务事务提交或回滚后需要继续执行其他操作而不是整个方法终止涉及异步、消息队列等复杂场景需要手动控制事务边界绝大多数场景用Transactional就够了。当它不够灵活时需要知道有编程式事务这个更灵活的工具。五、Spring事务和数据库事务的关系疑问Spring事务和数据库事务是什么关系Spring在这里加了什么回答Spring事务不是凭空产生的——它底层依赖数据库的事务能力。Spring做的不是在数据库之上再造一套事务机制而是统一管理何时开启、何时提交、何时回滚这套协调逻辑。Transactional → Spring层面的声明式事务 ↓ Spring的事务管理器PlatformTransactionManager ↓ 数据库连接Connection.setAutoCommit(false) ↓ 数据库事务InnoDB的ACID能力 Spring加了三层价值 1. 统一抽象不同数据库的事务API不同Spring屏蔽了差异 2. 传播行为控制事务在多个方法调用间的传递方式 3. 声明式注解Transactional让事务和业务代码彻底分离Spring事务本质是对数据库事务的封装和增强。底层还是用的数据库原生事务但Spring提供了传播行为、声明式注解、统一抽象等额外的管理能力。没有Spring你仍然可以通过Connection.setAutoCommit(false/true)手动控制事务——Spring让这个过程自动化、声明化。六、面试中这样回答面试官“Transactional是怎么实现的”回答框架“和AOP的原理一样——Spring为标注了Transactional的Bean生成代理对象。在代理对象中TransactionInterceptor拦截方法调用在方法前后通过事务管理器开启和提交/回滚事务。业务方法内部不需要写任何事务代码事务逻辑通过AOP从业务代码中剥离。这也解释了为什么自调用会失效——this调用绕过了代理对象事务拦截器无法生效。”面试官“Transactional在什么情况下会失效至少说三种。”回答“第一自调用——同一个类中方法互调this调用绕过了AOP代理。第二非public方法——CGLIB无法重写私有和受保护方法。第三异常被try-catch内部吞掉——事务拦截器完全感知不到异常直接提交。第四异常类型不匹配——Transactional默认只对RuntimeException和Error回滚CheckedException需要显式指定rollbackFor。第五数据库引擎不支持事务——比如MyISAMSpring事务管理无法在缺乏事务能力的引擎上生效。”总结Transactional通过TransactionInterceptorAOP代理实现——事务管理逻辑在代理对象中包裹业务方法业务代码与事务逻辑彻底分离传播行为定义事务在方法调用间的传递规则——REQUIRED同生共死REQUIRES_NEW独立提交。日志记录等辅助操作需要REQUIRES_NEW隔离失败影响自调用是最容易踩的坑——this调用绕过代理事务拦截器完全不知道你调用了它标注的方法。解决方式抽到另一个Service中或通过AopContext.currentProxy()获取代理对象异常被吞掉导致事务不回滚——Spring只在抛出异常时感知到失败。try-catch内部消化异常后拦截器看到方法正常返回直接提交事务声明式事务适合大多数场景需要方法内精细控制事务边界时用编程式事务。两者都是对数据库事务的封装和增强底层依赖数据库的事务能力完整的事务失效清单自调用绕过代理、非public方法、异常被吞掉、异常类型不匹配、MyISAM引擎——每次排查事务失效时按这个清单逐个排查下一篇预告Spring原理六——Spring用到了哪些设计模式从单例、工厂到代理、模板方法拆解Spring框架源码中的经典设计模式应用串联前五篇的IoC、AOP、MVC、自动配置、事务管理知识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2602284.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;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…