Transactional注解事务解析
@Transactional
注解的事务逻辑是通过Aop代理实现的。生成代理的过程比较复杂,这里只讨论事务的核心逻辑。
1、spring boot的事务管理器
spring boot提供了PlatformTransactionManager
类来管理事务
可以看到,spring boot只提供了接口,可以允许不同的数据源进行实现。
因为我平时主要使用的是jdbc,因此主要看jdbc的实现类DataSourceTransactionManager
。
DataSourceTransactionManager
类继承了AbstractPlatformTransactionManager
抽象类,而AbstractPlatformTransactionManager
抽象类又实现了PlatformTransactionManager
接口,类图关系如下:
2、几个重要的方法
在事务的执行过程中,一般的阶段为:开始事务->执行sql->提交事务/回滚事务。
所以当我们看DataSourceTransactionManager
类时,有几个方法是需要注意的:doBegin
,doCommit
,doRollback
另外还有其父类AbstractPlatformTransactionManager
的getTransaction
和startTransaction
方法
现在先记着这几个方法,后面再验证是否是使用到了这几个方法。
3、核心代码位置
从上面的几个方法中,我们可以大致推测,getTransaction
和startTransaction
以及doBegin
是用于开启事务的,doCommit
和doRollback
用于事务的提交和回滚。
实际上,
getTransaction
方法内部调用了startTransaction
,而startTransaction
内部调用的doBegin
我们开头提到,@Transactional
的事务是通过Aop代理实现的,那么执行的步骤就应该如下所示:
那么,是哪里拦截了@Transactional
修饰的方法(目标方法),并调用了开启事务的方法?
因为其代理生成的逻辑比较复杂,这里只找其核心代码。
在idea中,我们将源码下载下来,并使用ctrl
加鼠标左键依次点击开启事务的方法,就能发现,其调用的核心代码在TransactionAspectSupport
类中的createTransactionIfNecessary
方法中:
而createTransactionIfNecessary
方法又是由invokeWithinTransaction
方法调用的
invokeWithinTransaction
方法其实就是核心代码
4、核心代码分析
上面说了invokeWithinTransaction
方法就是核心代码,然后我们只取其中主要的部分进行分析:
1 | TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification); |
在上述代码中,执行步骤分别是:
- 执行
createTransactionIfNecessary
方法开启事务, - 使用
try
来执行invocation.proceedWithInvocation()
,这个就是执行目标方法 - 如果
try
的执行出现异常,捕获异常,并调用completeTransactionAfterThrowing
回滚 - 在
finally
时调用cleanupTransactionInfo
方法清除当前节点的事务信息 - 最后调用
commitTransactionAfterReturning
提交事务
ps:
completeTransactionAfterThrowing
方法中,并不是一定就会回滚,而是看捕获的异常是否是@Transactional
中声明的异常的子类,如果是的话就回滚,不是的话就提交
接下来我们打断点看看具体的流程:
首先进入了createTransactionIfNecessary
方法,如下:
然后createTransactionIfNecessary
调用了getTransaction
方法
getTransaction
方法又调用了startTransaction
方法
startTransaction
方法调用了doBegin
方法
然后我们可以看到,在doBegin
方法里,将事务的提交方式改成了手动提交
然后回到invokeWithinTransaction
方法,并调用目标函数
最后进行事务的提交