自定义异常触发事务回滚的关键在于抛出方式、配置规则和使用位置三者对齐:继承RuntimeException默认回滚,继承Exception需rollbackFor显式声明;异常必须从public方法边界抛出且不被吞没;必须由Spring代理的Bean调用,不可this.调用。

自定义异常要在事务回滚时准确触发,关键不在“怎么定义异常”,而在于“怎么抛、怎么配、怎么传”。只要三者对齐,Spring 就能感知并执行回滚。
先看异常类型:继承 RuntimeException 还是 Exception
这是最根本的分水岭:
- 若继承 RuntimeException(如
public class InsufficientStockException extends RuntimeException),默认就会触发回滚,无需任何额外配置 - 若继承 Exception(如
public class FileParseException extends Exception),默认不会回滚,必须显式声明@Transactional(rollbackFor = FileParseException.class)
再看抛出方式:异常必须“露头”,不能被吞掉
事务切面只拦截从方法边界抛出的异常。以下写法都会让回滚失效:
- 在方法内
try-catch后静默处理(不 re-throw、也不调用setRollbackOnly()) - 捕获后抛出新异常,但新异常未被
rollbackFor覆盖 - 异常发生在 private 方法中,而 public 方法未重新抛出
正确做法是:捕获后立即重抛原异常,或包装后明确 throw 出去,确保异常穿透到代理方法出口。
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
立即学习“Java免费学习笔记(深入)”;
最后看使用位置:必须落在 Spring 管理的 public 方法上
@Transactional 是基于动态代理的,只对满足条件的方法生效:
- 方法必须是 public 的
- 必须由 Spring 容器注入的 Bean 调用(避免 this. 调用导致 self-invocation 失效)
- 类本身需被 Spring 扫描管理(如加了 @Service、@Component)
例如,在 OrderService 中定义 @Transactional 方法,再由 Controller 注入调用——这才是有效路径。
补充技巧:手动标记回滚更可控
当无法或不便抛出异常(比如已有 try-catch 逻辑,又不想改调用方契约),可用编程方式强制回滚:
- 在 catch 块中调用:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); - 该操作会立即标记当前事务为“仅回滚”,后续即使没有异常也会 rollback
- 适合灰度降级、校验失败但需返回友好提示等场景

















