@Transactional注解的事务逻辑是通过Aop代理实现的。生成代理的过程比较复杂,这里只讨论事务的核心逻辑。

1、spring boot的事务管理器

spring boot提供了PlatformTransactionManager类来管理事务

可以看到,spring boot只提供了接口,可以允许不同的数据源进行实现。

因为我平时主要使用的是jdbc,因此主要看jdbc的实现类DataSourceTransactionManager

image-20240104121228007

DataSourceTransactionManager类继承了AbstractPlatformTransactionManager抽象类,而AbstractPlatformTransactionManager抽象类又实现了PlatformTransactionManager接口,类图关系如下:

image-20240104152235643

2、几个重要的方法

在事务的执行过程中,一般的阶段为:开始事务->执行sql->提交事务/回滚事务。

所以当我们看DataSourceTransactionManager类时,有几个方法是需要注意的:doBegindoCommitdoRollback

image-20240104123032371

另外还有其父类AbstractPlatformTransactionManagergetTransactionstartTransaction方法

image-20240104143534660

现在先记着这几个方法,后面再验证是否是使用到了这几个方法。

3、核心代码位置

从上面的几个方法中,我们可以大致推测,getTransactionstartTransaction以及doBegin是用于开启事务的,doCommitdoRollback用于事务的提交和回滚。

实际上,getTransaction方法内部调用了startTransaction,而startTransaction内部调用的doBegin

我们开头提到,@Transactional的事务是通过Aop代理实现的,那么执行的步骤就应该如下所示:

image-20240104145537752

那么,是哪里拦截了@Transactional修饰的方法(目标方法),并调用了开启事务的方法?

因为其代理生成的逻辑比较复杂,这里只找其核心代码。

在idea中,我们将源码下载下来,并使用ctrl加鼠标左键依次点击开启事务的方法,就能发现,其调用的核心代码在TransactionAspectSupport类中的createTransactionIfNecessary方法中:

image-20240104150341429

image-20240104150459657

createTransactionIfNecessary方法又是由invokeWithinTransaction方法调用的

image-20240104150659274

invokeWithinTransaction方法其实就是核心代码

4、核心代码分析

上面说了invokeWithinTransaction方法就是核心代码,然后我们只取其中主要的部分进行分析:

image-20240104152647626

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}

if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}

commitTransactionAfterReturning(txInfo);
return retVal;

在上述代码中,执行步骤分别是:

  1. 执行createTransactionIfNecessary方法开启事务,
  2. 使用try来执行invocation.proceedWithInvocation(),这个就是执行目标方法
  3. 如果try的执行出现异常,捕获异常,并调用completeTransactionAfterThrowing回滚
  4. finally时调用cleanupTransactionInfo方法清除当前节点的事务信息
  5. 最后调用commitTransactionAfterReturning提交事务

ps:completeTransactionAfterThrowing方法中,并不是一定就会回滚,而是看捕获的异常是否是@Transactional中声明的异常的子类,如果是的话就回滚,不是的话就提交

接下来我们打断点看看具体的流程:

首先进入了createTransactionIfNecessary方法,如下:

image-20240104160216341

然后createTransactionIfNecessary调用了getTransaction方法

image-20240104160348994

getTransaction方法又调用了startTransaction方法

image-20240104160634986

startTransaction方法调用了doBegin方法

image-20240104160717465

然后我们可以看到,在doBegin方法里,将事务的提交方式改成了手动提交

image-20240104160941509

然后回到invokeWithinTransaction方法,并调用目标函数

image-20240104161215730

最后进行事务的提交

image-20240104161329349

image-20240104161415514