阅读源码-@Transaction
阅读源码-@Transaction
@Transaction事务注解中rollBackFor和noRollbackFor底层执行逻辑
方法论
方法论-关注调用栈
栈底调用栈顶
通过调用栈找到了事务相关的方法
org.springframework.transaction.interceptor.TransactionInterceptor#invoke(MethodInvocation)
重新打上断点,恢复程序后,重新发送请求。从这里开始正向调试,从框架代码一步一步往业务代码执行。
步入方法,继续调试
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
方法论-开启Debug日志
通过观察日志的输出,寻找阅读源码突破口
1 | logging: |
寻找日志输出的地方,重新打上断点后,重新调试程序。
方法论-查看被调用的地方
直接查看注解在框架哪些地方被调用
当调用的地方比较少时,直接在调用的地方打上断点就可以
排除项目类、测试类、注释等后,继续找,找到SpringTransactionAnnotationParser类,但是该类仅仅是项目启动时解析Spring事务注解而已。
当以上几个地方打上断点时,仅仅在项目启动过程中断点起了作用,发起调用的时候并没有在断点处停留,说明发起调用时并不会触发这部分逻辑。
换个思路,@Transaction注解在项目启动时加载,但是注解中的值rollbackFor和noRollbackfor应该在程序调用的时被调用。
发现这些值只有在业务代码和注释上出现,但是我们发现注释上提到
org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn
遇到一个接口好多实现类的情况下,可以直接在接口上打上断点,重新调试后,断点会自动停留在具体执行的实现类上
在接口上打上方法断点,会走到这个实现类上中
org.springframework.transaction.interceptor.DelegatingTransactionAttribute#rollbackOn
观察此时的程序调用栈,寻找到了突破口
一些知名框架的JavaDoc注释中,有很多关键的@see和@link信息,并且是最权威的正确信息,比官方文档或者技术博客清晰稳妥。
搜索答案
寻找到了突破口即最开始的地方,接下来就是反复的调试,一步一步的往下调试。
从程序调用栈起点开始
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
选择步入方法
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
重点逻辑如下:执行业务逻辑代码,然后基于业务逻辑处理结果,然后去走不同的逻辑。
抛出异常,执行方法:completeTransactionAfterThrowing()
正常执行完毕,执行方法:commitTransactionAfterReturning()
步入completeTransactionAfterThrowing后,继续调试,可以看到当前事务和当前异常的相关的信息
在方法里,大体逻辑如下
其中判断当前抛出异常是否要回滚,就是这里的重中之重。
调试过程中,遇到if-else语句要注意去构建不同的案例,以覆盖尽量多的代码逻辑。
步入判断逻辑方法中
org.springframework.transaction.interceptor.DelegatingTransactionAttribute#targetAttribute
继续步入方法
org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn
这里就是就是判断该异常是否回滚的具体逻辑:
核心逻辑:rollbackRules中配置了注解中的回滚规则,通过循环,拿到我们程序中抛出的异常ex,去匹配规则,最好选择一个winner
如果winner为空,则走默认逻辑,如果是RuntimeException 或者是 Error 的子类,就要进行回滚。即当没有配置rollbackFor与noRollbackFor的情况下,如果抛出的异常属于RuntimeException 或者 Error 的子类,则进行回滚操作
如果winner不为空,则查看winner是否属于NoRollbackRuleAttribute即不用回滚的配置,如果是则返回false,表示不用执行回滚操作
如今的核心的问题是winner怎么来的,答案就就在如下递归调用中
核心理论就是:如果配置的规则不为空,则判断当前抛出的异常和配置的规则中的 rollbackFor 和 noRollbackFor 谁距离更近。这里的距离是指父类与子类之间的关系。将更近的作为winner放到return !(winner instanceof NoRollbackRuleAttribute);中进行判断
抛出的是 RuntimeException,它距离 noRollbackFor=RuntimeException.class 为 0。RuntimeException 是 Exception 的子类,所以距离 rollbackFor = Exception.class 为 1。winner