记录一次Spring事务线上异常
Spring事务管理配置方式:
- XML模糊匹配,绑定事务管理
- 注解,可对每个需要进行事务处理的方法单独配置,只需 @Transactional,然后添加属性配置
为简便,本文使用注解方式。Spring初始化时,会通过扫描拦截对事务的方法进行增强。若目标方法存在事务,Spring就会创建一个Bean对应的代理(Proxy)对象,并进行相关的事务处理操作。
1 setup
1.1 数据库配置文件 jdbc.properties
1.2 JDBC配置类
从 jdbc.properties 加载相关配置项,并创建 JdbcTemplate、DataSource、TransactionManager 等相关Bean。
1.3 应用配置类
通过注解,配置了数据源、MyBatis Mapper 的扫描路径以及事务等。
2 unchecked 异常与事务回滚
用户管理功能,每位用户注册后,都往数据库里存入信息:
Mapper类:
数据库表Schema:
业务类 StudentService包括一个保存的方法 saveStudent。执行一下保存,一切正常。
测试该事务是否回滚:若发现用户名=JavaEdge,抛异常,触发事务回滚。
测试保存我这个用户:
执行结果打印出了这样的信息:
异常抛了,但观察到DB还是有条新记录。
那为何异常也抛了,却没有回滚?
3 源码解析
顺着 saveUser debug:
看到 CglibAopProxy,事务本质上也是一种特殊切面,在创建过程中,被 CglibAopProxy 代理。 事务处理拦截器是 TransactionInterceptor支撑整个事务功能的架构
TransactionInterceptor如何实现事务特性
执行代理类的目标方法时,触发invoke()
。跳到异常处理。catch到异常时,调 completeTransactionAfterThrowing进一步处理。
TransactionAspectSupport#invokeWithinTransaction
completeTransactionAfterThrowing.rollbackOn()
事务回滚判断条件。条件满足时,即会触发事务回滚。
RuleBasedTransactionAttribute#rollbackOn()
RuleBasedTransactionAttribute自身的rollbackOn()
当在 @Transactional 配置 rollbackFor,该方法就会用捕获到的异常和 rollbackFor 中配置的异常比对:
- 所捕获异常是 rollbackFor 配置异常,直接 rollback
案例中,没有加任何规则,所以找不到规则去处理(所以 winner == null
),进而走到下一步。
DefaultTransactionAttribute 的 rollbackOn()
当发生如下 case:
- 没有在 @Transactional 配置 rollback 属性
- 或者,捕获到的异常和所配置异常类型不一
就调用父类rollbackOn():
只有异常类型为 RuntimeException 或 Error,才true =》才触发 completeTransactionAfterThrowing#rollback =》事务才回滚:
综上,Spring 处理事务时,若没有在 @Transactional 配置 rollback 属性,则只有捕获到 RuntimeException 或 Error 才会触发事务回滚。 而案例抛 Exception,又未指定回滚规则,所以未触发回滚。
4 修正
将所抛异常类型改成 RuntimeException:
这种修改方法不优雅,毕竟异常有时就是固定死不能修改。还有更好方案。
解析 RuleBasedTransactionAttribute#rollbackOn 的代码时提到过 rollbackFor 属性的处理规则。在 @Transactional 的 rollbackFor 加入要支持的异常类型(在这是 Exception)即可匹配上我们所抛异常。完善注解配置即可:
相关文章
- Spring学习笔记(十)——SpringMVC框架中的拦截器和异常处理
- Spring MVC更多家族成员---框架内异常处理与HandlerExceptionResolver---09
- Spring cloud多模块开发下Feign的使用,以及@FeignClient注入bean找不到异常解决「建议收藏」
- Spring Boot + Web Socket 打造实时监控异常,写得太好了!
- 详解Java中的Spring框架
- Spring条件装配
- Spring StoredProcedure调用Oracle函数各种异常解决方法
- spring异常:No runnable methods 解决方法
- Spring中11个最常用的扩展点,你知道几个?
- Spring Cloud zuul自定义统一异常处理实现
- Spring Boot 2.4版本前后的分组配置变化及对多环境配置结构的影响
- 【Spring专题】「技术原理」从源码角度去深入分析关于Spring的异常处理ExceptionHandler的实现原理
- Spring Cloud Security进行安全审计(二)
- 【愚公系列】2023年04月 Java教学课程 141-Spring MVC框架的异常处理
- 【深入浅出Spring原理及实战】「源码调试分析」深入源码探索Spring底层框架的的refresh方法所出现的问题和异常
- Spring MVC异常处理
- spring的AOP(五)—-Spring AOP 编程(AspectJ )详解编程语言
- Spring Boot2.0之整合Mybatis详解编程语言
- Spring Boot实现热部署详解编程语言
- Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例详解编程语言
- Spring Boot(十四):spring boot整合shiro-登录认证和权限管理详解编程语言
- Spring Boot(十二):spring boot如何测试打包部署详解编程语言
- 在静态方法里调用spring注入的方法详解编程语言
- Spring MVC中@Controller和@RequestMapping注解详解
- Spring Boot 2.x 系列教程:WebFlux REST API 全局异常处理 Error Handling
- Spring实现文件上传(示例代码)