Spring默认对RuntimeException及其子类自动回滚事务,但需确保异常不被静默捕获、传播路径不脱离代理,并通过rollbackFor/noRollbackFor精准控制回滚行为,兼顾日志可追溯与传播行为适配。

在 Spring 的事务管理中,RuntimeException 及其子类默认会触发事务回滚,这是 Spring 的默认行为,无需额外配置。但“正确处理”不仅指让它自动回滚,更包括明确意图、避免误判、控制传播行为和日志可追溯性。
理解默认回滚规则
Spring 的 @Transactional 默认只对未检查异常(即 RuntimeException 和 Error)执行回滚;对受检异常(如 IOException、SQLException)则不会自动回滚。
- 抛出
NullPointerException、IllegalArgumentException、自定义的ServiceException extends RuntimeException→ 自动回滚 - 抛出
Exception(非 RuntimeException)、FileNotFoundException→ 不回滚,除非显式声明rollbackFor = Exception.class
避免吞掉 RuntimeException 导致事务失效
常见错误是在 service 方法中捕获了 RuntimeException 却没重新抛出,导致事务切面“以为一切正常”,从而提交事务:
- ❌ 错误写法:
try { doDbOperation(); } catch (RuntimeException e) { log.error("忽略异常", e); }→ 事务已提交,数据不一致 - ✅ 正确做法:不捕获,或捕获后重新抛出(或包装后抛出):
catch (RuntimeException e) { log.error("操作失败", e); throw e; // 或 throw new ServiceException("业务失败", e); }
按需定制 rollbackFor 和 noRollbackFor
当业务逻辑需要更精细控制时,应显式声明:
立即学习“Java免费学习笔记(深入)”;
- 让某个受检异常也触发回滚:
@Transactional(rollbackFor = IOException.class) - 阻止某个 RuntimeException 回滚(极少见,慎用):
@Transactional(noRollbackFor = ValidationException.class) - 多个异常类型可写成数组:
rollbackFor = {SQLException.class, TimeoutException.class}
注意事务传播行为与嵌套调用
如果一个 @Transactional 方法内部调用另一个同样标注了 @Transactional 的方法(且是本类内直接调用),后者事务注解不会生效(因代理未介入),此时即使它抛出 RuntimeException,也不会影响外层事务边界。
- ✅ 推荐:通过 self-injection 或提取到独立 Bean 中,确保事务代理生效
- ✅ 日志中记录事务方法入口/出口及异常,便于排查“为何没回滚”
- ⚠️ 注意:
REQUIRES_NEW传播下,内层事务异常只回滚自身,不影响外层 —— 此时需评估是否真要隔离
不复杂但容易忽略:事务生效的前提是异常最终**穿透事务代理方法边界**。只要 RuntimeException 没被静默吃掉、传播路径上没脱离代理,Spring 就会按规则回滚。


















