SpringFramework @Transactional
@Transactional注解对于Java后端开发者来说是一个经常接触到的注解,其所提供的声明式事务可以让程序员们很方便的使用注解。
但是由于它封装的过好,导致其中隐含了很多的小坑,作为一个小白,最近也踩了不少坑。虽然已经有很多相关的文章,但是为了提升自己,更好的掌握这方面的原理和知识,就简略查看了一下@Transactional的相关源码,并将收获总结如下,也当作是一个记录。
@Transactional的实现原理还是通过了Spring AOP技术实现的,所以只能使用在public方法上。
在一个被@Transactional注解了的方法上打上断点,可以看到调用栈如下
关于Spring AOP
其实直白一点就是Spring使用了动态代理,用代理将符合条件的类给wrap住,再自定义AOP操作即可。
不同的SpringBoot版本,对于AOP的默认实现是不同的
- SpringBoot1.x 默认使用JDK动态代理
- SpringBoot2.x 由于JDK动态代理可能导致类型转化异常而默认使用CgLib。如果需要改为JDK动态代理,那么通过配置文件,指定spring.aop.proxy-target-class=false来进行修改。proxyTargetClass配置已经无效了。
- SpringBoot3.x 猜测也是使用的CgLib
更多AOP的相关不再展开,后续再填坑。
深入@Transactional
可以根据上面的调用栈,将栈里的方法分成几部分
- part1 这里就是HTTP方法的入口
- part2 这里就是AOP代理的部分
- part3 这里就是AOP代理的逻辑部分
- part4 这里就是@Transactional方法的入口
关注到TransactionAspectSupport,可以看到这就是AOP的一个支撑类,根据调用栈找到invokeWithinTransaction方法,核心逻辑如下
可以看到,首先会进行AOP方法的执行,然后如果抛出异常,进入completeTransactionAfterThrowing,点进这个方法
可以看到,进行一系列的判断,根据判断结果执行rollback或者commit进行数据库事务操作。
那么,这个是如何触发的呢?写一个会抛异常的@Transactional方法,然后在这个completeTransactionAfterThrowing方法打上断点,然后逐步往下调试后,可以看到
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {...}
这个判断,txInfo是这个Support的一个内部类,结构如下,了解即可
重点在于后面的 rollbackOn(ex)这一句,点进去发现是一个接口的API,那么继续根据断点往下调试,调用栈会跳到这个 rollbackOn方法,发现是在类DelegatingTransactionAttribute里面的,
再往下调试,就会跳到 RuleBasedTransactionAttribute,光看这个类名就会觉得应该是找对地方了,可以看到
这边先定义了一个winner和一个deepest,是不是觉得很奇怪,往下看就好。后面判断 rollbackRules这个集合是否为null,可以马上联想到@Transactional中定义的
rollbackFor、noRollbackFor等等,通过debug也可以看到,我们自定义的rollbackFor和noRollbackFor都被封装到了这里,然后就是遍历这些rule,但是最后只有一个winner,
那么看到winner赋值跟rule的depth有关,看到getDepth方法如下
class RollbackRuleAttribute {
// 这个是回滚规则绑定的Exception的Class全限定名,比如java.lang.RuntimeException
private final String exceptionPattern;

...
// return the depth of the superclass matching, with the following semantics.
// -1: meas this rule does not match the supplied exception.
// 0: means this rule matches the supplied exception directly.
// any other positive value means this rule matches the supplied exception
// within the superclass hierarchy, where the value is the number of levels
// in the class hierarchy between the supplied exception and the exception
// against which this rule matches directly.
// 就是看当前抛出的异常和配置的规则中的rollbackFor和noRollbackFor谁距离更近
// 距离更近指的是: 父类和子类之间的关系
// 比如抛出的异常是 RuntimeException
// @Transactional(rollbackFor = Exception.class, noRollbackFor = RuntimeException.class)
// 那么距离 noRollbackFor的距离为0
// 距离 rollbackFor的距离为1
// 那么这样返回的就是 noRollbackFor的depth
private int getDepth(Throwable exception) { do something };
}
通过这里的getDepth方法,获取到距离最近的rollbackRule,也就是说,这个被抛出的异常跟哪个rule最搭边,那么这个winner就是谁。
如果winner为空,也就是说没有匹配的rule或者rule为空,
那么调用RuleBasedTransactionAttribute的父类的rollbackOn方法,点进去可以看到非常的简短
return (ex instanceof RuntimeException || ex instanceof Error);
所以可以看到,@Transactional默认会为RuntimeException和Error回滚。
如果winner不为空,那么取得到winner后,就去判断
return !(winner instanceof NoRollbackRuleAttribute));
见名知意,就是看这个winner rule是不是要求回滚,很简单的判断。至此,@Transactional的工作也就完成了。