news 2026/5/8 19:31:33

Java中高级面试题详解(十三):彻底搞懂 Spring 事务传播机制,别再只会用 @Transactional!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java中高级面试题详解(十三):彻底搞懂 Spring 事务传播机制,别再只会用 @Transactional!

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

在企业级开发中,事务管理是数据一致性的生命线。但很多开发者对@Transactional的理解仅停留在“加了就能回滚”,却在真实场景中频频踩坑:

  • 为什么方法内部调用事务不生效?
  • 为什么 try-catch 后事务没回滚?
  • 多个@Transactional方法嵌套时,到底用哪个事务?

今天我们就通过7 种传播行为 + 5 大实战陷阱 + 源码解析,彻底掌握 Spring 事务的底层逻辑!


一、需求场景:用户下单涉及多个操作

@Service public class OrderService { @Autowired private AccountService accountService; @Transactional public void createOrder(Long userId, BigDecimal amount) { // 1. 扣减账户余额 accountService.deductBalance(userId, amount); // 2. 创建订单 orderMapper.insert(new Order(userId, amount)); // 3. 发送消息(可能失败) messageService.sendOrderCreated(userId); } }

问题来了:

  • 如果sendOrderCreated()抛异常,账户扣款会回滚吗
  • 如果deductBalance()内部也有@Transactional事务如何传播

答案取决于事务传播机制(Propagation)


二、Spring 事务的 7 种传播行为(重点!)

传播行为说明典型场景
REQUIRED(默认)如果当前有事务,加入;否则新建绝大多数业务
REQUIRES_NEW挂起当前事务,新建独立事务日志、审计、独立操作
SUPPORTS有事务则用,无则非事务执行查询类方法
NOT_SUPPORTED挂起事务,以非事务方式执行高性能读、外部回调
MANDATORY必须在事务中执行,否则抛异常强一致性子操作
NEVER不能在事务中执行,否则抛异常幂等校验、缓存更新
NESTED嵌套事务(依赖数据库 savepoint)部分回滚需求

💡最常用的是 REQUIRED 和 REQUIRES_NEW


三、实战演示:REQUIRED vs REQUIRES_NEW

场景:主流程扣款 + 子流程记日志

@Service public class OrderService { @Autowired private LogService logService; @Transactional(propagation = Propagation.REQUIRED) public void createOrder() { orderMapper.insert(...); logService.log("订单创建"); // 调用日志服务 throw new RuntimeException("模拟失败"); } } @Service public class LogService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void log(String msg) { logMapper.insert(msg); // 独立事务 } }

✅ 结果:

  • 订单插入回滚(主事务失败);
  • 日志记录成功提交(子事务独立)。

🔥 如果log()也用REQUIRED,则日志也会回滚!


四、五大高频陷阱与解决方案

❌ 陷阱1️⃣:方法内部调用,事务失效!

@Service public class UserService { public void register(User user) { this.saveUser(user); // 直接调用,事务不生效! } @Transactional public void saveUser(User user) { userMapper.insert(user); throw new RuntimeException(); } }

原因
Spring 事务基于AOP 代理
register()调用saveUser()this. 调用,绕过了代理对象,事务拦截器未触发。

✅ 解决方案:

  • 方案1:注入自己(不推荐)
    @Autowired private UserService self; self.saveUser(user);
  • 方案2:拆到另一个 Service(推荐)
  • 方案3:使用AopContext.currentProxy()(需开启 exposeProxy)

❌ 陷阱2️⃣:try-catch 吞掉异常,事务不回滚!

@Transactional public void transfer() { try { accountMapper.deduct(...); accountMapper.add(...); } catch (Exception e) { log.error("转账失败", e); // 忘记 throw!事务不会回滚! } }

✅ 正确做法:

} catch (Exception e) { log.error("...", e); throw e; // 必须抛出! }

或显式回滚:

@Transactional(rollbackFor = Exception.class) public void transfer() { try { ... } catch (Exception e) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }

⚠️ 默认只对RuntimeException 和 Error回滚!检查异常(如 IOException)不会回滚!


❌ 陷阱3️⃣:非 public 方法,事务失效!

@Transactional protected void saveUser() { ... } // protected 方法,代理无效!

✅ 必须是public 方法


❌ 陷阱4️⃣:异步方法中事务失效!

@Transactional @Async public void asyncProcess() { // 事务不会生效!因为 @Async 和 @Transactional 代理冲突 }

✅ 解决:在调用处加事务,异步方法内不加:

@Transactional public void process() { asyncService.doWork(); // 异步方法无 @Transactional } @Service public class AsyncService { @Async public void doWork() { ... } }

❌ 陷阱5️⃣:MySQL 表引擎不是 InnoDB!

MyISAM 不支持事务!确保表引擎为InnoDB


五、源码级原理:事务如何工作?

  1. 启动时@EnableTransactionManagement注册BeanPostProcessor
  2. 创建 Bean 时:为带@Transactional的类生成CGLib 代理
  3. 方法调用时
    • 代理拦截方法;
    • TransactionManager获取连接;
    • 设置autoCommit=false
    • 执行业务逻辑;
    • 无异常 → commit;有异常 → rollback。

🔑 关键:同一个数据库连接在整个事务中复用(通过 ThreadLocal)。


六、面试加分回答

问:REQUIRES_NEW 和 NESTED 有什么区别?

✅ 回答:

  • REQUIRES_NEW:完全独立的新事务,提交/回滚互不影响;
  • NESTED:嵌套在当前事务中,使用数据库savepoint实现部分回滚。
    • 外层回滚 → 内层也回滚;
    • 内层回滚 → 只回滚到 savepoint,外层可继续提交。

NESTED 依赖数据库支持(如 MySQL InnoDB),而 REQUIRES_NEW 是通用方案。

问:事务失效的根本原因是什么?

✅ 回答:

核心是代理未生效
Spring 事务基于 AOP 代理,只有通过代理对象调用 public 方法时,
拦截器才能织入事务逻辑。
自调用、非 public、静态方法、final 方法等都会导致代理失效。


七、最佳实践建议

  • ✅ 事务方法必须是public
  • ✅ 避免自调用,拆分到不同 Service;
  • ✅ 显式指定rollbackFor = Exception.class
  • ✅ 谨慎使用REQUIRES_NEW,避免连接耗尽;
  • ✅ 事务方法尽量短小,减少锁持有时间;
  • ✅ 读多写少场景,可用readOnly = true提升性能。

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 0:45:18

【Java毕设全套源码+文档】基于Java旅游民宿信息管理系统的设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/5/1 7:54:38

【强化学习】第四章:动态规划(DP)

【强化学习】第四章:动态规划(DP) 说明:学习本篇时一定一定要认真学完 https://blog.csdn.net/friday1203/article/details/155533020?spm1001.2014.3001.5501 ,因为动态规划就是为了求解MDP问题的。所以你首先要非常清晰什么是MDP、MDP框架…

作者头像 李华
网站建设 2026/4/19 18:08:47

AutoGPT能否用于法律文书起草?专业度测评

AutoGPT能否用于法律文书起草?专业度测评 在律师事务所的某个深夜,一位年轻律师正对着屏幕反复核对一份股权转让协议中的条款引用——这已是本周第三份类似合同。他需要确认《公司法》第七十二条是否仍适用、地方工商登记要求有无更新、同类判例中法院如…

作者头像 李华
网站建设 2026/5/8 11:52:46

AutoGPT运行资源消耗测试:需要多少GPU显存?

AutoGPT运行资源消耗测试:需要多少GPU显存? 在当前AI技术快速演进的背景下,大型语言模型(LLM)正从被动应答工具向具备自主决策能力的智能体转型。像AutoGPT这样的开源项目,已经能够基于一个简单目标——比如…

作者头像 李华
网站建设 2026/5/8 5:29:17

椭圆曲线的“加法”群规则

这四个式子是在讲椭圆曲线的“加法”群规则(chord-and-tangent)。核心口诀是: 同一条直线与椭圆曲线的三个交点(按重数计算)相加等于 0(单位元) 也就是:若直线与曲线交于 A,B,C,则 A+B+C=0。 这里的 0(图里写 0)指的是无穷远点 O,是加法单位元。 同时,点的相反数是…

作者头像 李华
网站建设 2026/5/2 10:56:45

支持多模型接入的LobeChat,如何实现低成本高回报的Token售卖?

支持多模型接入的LobeChat,如何实现低成本高回报的Token售卖? 在AI应用爆发式增长的今天,越来越多企业开始尝试将大语言模型(LLM)集成到自己的产品中。然而,直接调用闭源API成本高昂,而自建系统…

作者头像 李华