news 2026/4/28 8:05:51

为什么 @Transactional 在同类调用中失效?一文讲透 Spring 事务代理机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
为什么 @Transactional 在同类调用中失效?一文讲透 Spring 事务代理机制

一、前言

在使用 Spring Boot 开发时,很多人都会遇到一个经典问题:

为什么 @Transactional 明明加了,但事务就是不生效?

尤其是这种情况:

public void A() { B(); // B上有@Transactional }

👉事务直接失效 ❌

这其实是 Spring 事务最核心、也是最容易踩坑的点。

二、先看一个真实踩坑案例

@Service public class UserServiceImpl implements UserService { @Override public void test() { doTran(); // 内部调用 } @Transactional public void doTran() { userMapper.insert("李四"); int i = 1 / 0; // 异常 userMapper.insert("王五"); } }

你以为结果是:

李四 ❌ 回滚
王五 ❌ 不执行

实际结果是:

李四 ✅ 插入成功(没有回滚)

👉 ❗ 事务失效!

三、核心原因(本质)

🔥Spring 事务 = AOP + 代理模式


1️⃣ Spring 是怎么实现事务的?

当你写:

@Transactional public void doTran() {}

Spring 并不是直接在这个方法上加事务,而是:

创建一个代理对象(Proxy)

2️⃣ 真正执行流程是:

外部调用 ↓ 代理对象(Proxy) ↓ 开启事务 目标方法 doTran() ↓ 异常 / 正常 ↓ 提交 or 回滚

四、问题的关键:同类调用

错误调用路径

Controller ↓ test()【代理对象】 ↓ this.doTran() ❌ ↓ 原始对象执行(绕过代理) ↓ @Transactional 失效

核心问题一句话:

同类内部调用(this.xxx)不会走代理对象

五、用一张图彻底理解

✅ 正常事务调用

Controller ↓ Proxy(代理) ↓ 开启事务 doTran() ↓ 提交 / 回滚

❌ 同类调用

Controller ↓ Proxy(代理) ↓ test() ↓ this.doTran() ❌ ↓ 直接执行(无事务)

六、为什么会这样?

因为:

AOP 是基于代理的

而:

this.xxx() =直接调用原始对象

👉 完全绕过代理

七、解决方案(3种)

✅ 方案1(推荐):事务加在外层方法

@Override @Transactional public void test() { doTran(); }

调用链:

Controller → test(代理) → doTran(在事务中执行)

👉 ✔ 正常回滚

✅ 方案2:通过代理对象调用自己

@Autowired private UserService userService; public void test() { userService.doTran(); // 走代理 ✅ }

解释:

@Autowired 注入的不是原始对象

@Autowired private UserService userService;

👉 你以为拿到的是:UserServiceImpl 原始对象 ❌

👉 实际上拿到的是:UserServiceImpl 的代理对象(Proxy)✅

@Autowired 注入的是 Spring 创建的代理对象,而不是原始对象,所以通过它调用方法时会触发事务增强。

✅ 方案3(了解):AopContext

((UserService) AopContext.currentProxy()).doTran();

👉 ❗ 不推荐(侵入性强)

八、常见事务失效场景(面试高频)


❌ 1. 同类调用

this.xxx(); // ❌


❌ 2. private 方法

private void doTran() {} // ❌ 无法代理


❌ 3. final 方法

public final void doTran() {} // ❌ 无法增强


❌ 4. 异常被吃掉

try {
int i = 1/0;
} catch (Exception e) {}

👉 ❗ 不会回滚


❌ 5. 抛 checked exception

throw new Exception(); // ❌ 默认不回滚


九、事务回滚规则(必须记住)

✔ RuntimeException → 回滚
✔ Error → 回滚
❌ Exception(受检异常)→ 默认不回滚


十、工程级最佳实践


✅ 标准写法

@Service public class UserService { @Transactional public void createUser() { insertUser(); insertProfile(); insertPointLog(); } }

核心原则:

事务加在“业务编排方法”上

而不是:加在内部小方法上

十一、一句话总结(面试必备)

🔥 Spring 事务基于 AOP 代理实现,只有通过代理对象调用的方法,@Transactional 才会生效;同类内部调用会绕过代理,导致事务失效。

十二、口诀(强烈建议记住)

  • 事务要生效,必须走代理
  • 类内直接调,事务全失效
  • 异常要抛出,回滚才生效

十三、延伸(进阶方向)

后续可以继续深入:

✔ 事务传播机制
✔ 事务 + Redis
✔ 事务 + MQ(最终一致性)
✔ 分布式事务

结尾

如果你踩过这个坑,说明你已经开始:

🔥 从“会用 Spring” → “理解 Spring 底层机制”

这一步非常关键。

下一篇:

《Spring 事务10大面试坑(含this调用、try-catch失效、线程问题)》

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

AI客服是做什么的?这套PHP源码系统讲清楚了:多模态+人工转接

温馨提示:文末有资源获取方式一、什么是AI客服?它到底能做什么?很多人对AI客服的印象还停留在“自动回复机器人”阶段。实际上,一套成熟的AI客服系统能干的事情远比你想象的多:724小时无人值守:凌晨三点客户…

作者头像 李华
网站建设 2026/4/28 8:04:44

ChatTTS高性能调优:多线程并发下的响应速度优化

ChatTTS高性能调优:多线程并发下的响应速度优化 1. 项目背景与性能挑战 ChatTTS作为目前开源界最逼真的语音合成模型之一,在中文对话场景中表现出色。其独特的停顿、换气声和笑声生成能力,让合成语音听起来完全不像机器人。然而&#xff0c…

作者头像 李华
网站建设 2026/4/28 8:04:33

S2-Pro构建技术博客助手:从大纲到成文的自动化写作流程

S2-Pro构建技术博客助手:从大纲到成文的自动化写作流程 1. 技术博主的内容创作痛点 技术博客创作从来不是件轻松的事。我见过太多同行在深夜对着空白文档发呆,明明满脑子技术干货,却卡在如何组织语言、确保代码准确、保持文风统一这些看似简…

作者头像 李华
网站建设 2026/4/28 8:03:23

基于Git的开发者环境配置同步工具copaw详解与实践

1. 项目概述与核心价值最近在折腾一个挺有意思的项目,叫copaw,是 GitHub 上一个名为jackxiong11894的开发者开源的工具。乍一看这个名字,可能会有点摸不着头脑,但如果你经常需要在不同环境、不同机器之间同步你的命令行配置、脚本…

作者头像 李华
网站建设 2026/4/28 8:03:23

Flux局部重绘1——学习路线

目录 一、前言 二、Flux局部重绘学习路线 一、基础理论层(必须先掌握) 1.1 扩散模型基础 1.2 Flow Matching / Rectified Flow(FLUX 核心训练范式) 1.3 DiT (Diffusion Transformer) 架构 二、FLUX 基础模型层(…

作者头像 李华