如果你最近也在准备 Java 面试,我整理了一套:
✔ 高频Java面试题(含详细解析)
✔ 模拟真实面试流程训练
✔ 简历优化建议(适合1-5年经验)
已经全部整理成在线版本,方便查阅和练习。
👉 需要的话可以自取:
https://www.myquotego.com

在面试中,Spring 事务失效是一个高频考点,很多候选人回答“事务没加注解”或者“事务传播问题”,却忽略了底层原理导致的真正原因。本文将从技术原理、代码示例到实际应用场景,帮你彻底弄懂Spring事务失效的根本原因。
我整理了一套完整Java面试题库,
完整版在我的技术站:
https://www.myquotego.com/html/questions?_from=csdn_123_1
一、问题背景
在企业级Java应用中,事务管理是保证数据一致性和完整性的核心手段。Spring 提供了基于 AOP 的声明式事务管理,通常通过 @Transactional 注解实现。但在实际开发中,经常会遇到事务没有生效的情况,例如:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
// 模拟异常
if (true) {
throw new RuntimeException("测试事务回滚");
}
}
}
预期:userRepository.save(user) 会回滚
实际:数据仍然插入数据库
为什么会出现这种情况?很多人只停留在“事务没加注解”或“异常没捕获”的表面原因,而忽略了 Spring 事务的底层机制。
二、技术原理解析
1. Spring 事务的底层实现
Spring 事务是通过 AOP(代理模式) 实现的。Spring 通过生成代理对象,在方法执行前开启事务,方法执行后提交或回滚事务。
- JDK 动态代理:当目标类实现接口时,Spring 默认使用 JDK 动态代理,只代理接口方法。
- CGLIB 代理:当目标类没有实现接口时,Spring 使用 CGLIB 生成子类代理。
核心流程:
- 创建代理对象
- 调用被代理方法时,事务拦截器 (
TransactionInterceptor) 先开启事务 - 方法执行过程中出现异常,则回滚事务
- 方法正常返回,则提交事务
2. 事务失效的常见原因
原因一:同类内部调用
Spring 事务是基于代理的,如果 同类内部方法调用,事务不会生效,因为调用没有经过代理对象。
@Service
public class UserService {
@Transactional
public void createUser(User user) {
userRepository.save(user);
updateUser(user); // 内部调用,事务不会生效
}
@Transactional
public void updateUser(User user) {
user.setName("Updated");
userRepository.save(user);
}
}
分析:
updateUser的事务注解不会生效,因为createUser内部直接调用,没有经过 Spring 代理。
原因二:异常类型
Spring 默认只对 RuntimeException(及其子类) 回滚,对 Checked Exception 不回滚。
@Transactional
public void saveData() throws IOException {
// do something
throw new IOException("Checked异常不会回滚");
}
解决方法:
@Transactional(rollbackFor = Exception.class)
原因三:事务注解位置不当
@Transactional只能放在 public 方法 上- private、protected、default 方法不被代理拦截
3. 事务传播行为与失效场景
Spring 提供了多种事务传播行为,理解传播行为有助于避免事务失效:
| 传播类型 | 描述 | 典型问题 |
|---|---|---|
| REQUIRED | 如果有事务,加入;没有就新建 | 常用 |
| REQUIRES_NEW | 总是新建事务 | 内部调用可能覆盖外部事务 |
| NESTED | 嵌套事务 | 需要数据库支持 savepoint |
举例:内部调用
REQUIRES_NEW时,外部事务可能被暂停,导致对数据的回滚感知不同。
三、代码示例
下面给出一个完整示例,演示事务失效与修复:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserService selfProxy; // 使用Spring注入自身代理
// 正确生效事务
@Transactional
public void createUser(User user) {
userRepository.save(user);
try {
selfProxy.updateUser(user); // 通过代理调用,事务生效
} catch (Exception e) {
e.printStackTrace();
}
if (true) {
throw new RuntimeException("测试回滚");
}
}
@Transactional(rollbackFor = Exception.class)
public void updateUser(User user) {
user.setName("Updated");
userRepository.save(user);
}
}
核心技巧:
- 自引用注入自身代理,避免内部调用事务失效
rollbackFor明确指定回滚异常- 保证方法为
public
四、实际应用场景
- 电商订单系统:订单创建涉及库存扣减、支付、物流更新,多操作需要保证事务一致性。
- 金融交易系统:资金转账要求严格的事务控制,任何异常必须回滚。
- 批量数据处理:内部方法调用可能导致事务失效,需要使用代理调用或事务传播策略。
我整理了一套完整Java面试题库,
完整版在我的技术站:
https://www.myquotego.com/html/questions?_from=csdn_123_1
五、总结
Spring 事务失效的根本原因主要在于 代理机制与调用路径,常见失效原因包括:
- 内部方法调用
- 非 RuntimeException 异常
- 方法访问修饰符不正确
- 事务传播不理解或使用不当
掌握这些原理,面试官的问题就能迎刃而解,开发中也能避免高并发业务下的数据不一致问题。
我整理了一套完整Java面试题库,
完整版在我的技术站:
https://www.myquotego.com/html/questions?_from=csdn_123_1
关注我,持续更新Java面试核心知识。

2255

被折叠的 条评论
为什么被折叠?



