本文着重分析 Spring 声明式事务 @Transactional 的实现原理。学习 Spring 事务首先要知道 spring-tx 和 spring-jdbc 是两个工程,换句话说 Spring 事务和 jdbc 它们是彻底解耦的。
- Spinrg-AOP:Spring 事务是 AOP 的典型应用。
- PlatformTransactionManager:Spring 是如何接管事务的。
- 同一个连接保证:Spring 事务和用于业务处理的的连接是如何保证是同一个?如果 connection 都不是同一个,事务更无从谈起。
1. 工作原理
首先,我们先看一下原生的 jdbc 是如何处理事务的。之后,我们再分析 Spring 是如何接管事务。1.1 jdbc 事务
经典的 jdbc 事务可以分为三部分:第一步先获取连接 connection,并关闭自动提交;第二步执行业务逻辑;第三步根据是否抛出异常选择回滚或提交事务。
想像一下,如果是你来设计一个框架接管所有的事务,你会怎么做呢?Connection connection = getConnection();
connection.setAutoCommit(false);
-------------------- 准备事务 ------------------------
try {
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from demo_table");
------------------ 提交或回滚事务 --------------------
} catch (SQLException e) {
connection.rollback();
} finally {
connection.commit();
}
- 首先,这是典型的 AOP 应用,可以使用面向切面编程来消除模板代码。整个事务处理的过程分为三部分:
- 第一步:获取事务,包括获取连接,以及关闭自动提交等配置。
- 第二步:通过反射执行业务代码。
- 第三步:根据是否抛出异常,提交或回滚事务。
其次,问题就来了:使用 AOP 后,如何保证处理事务的 connection 和中间处理业务的 connection 是同一个呢?Spring 使用了一个最简单的办法,将** connection 保存**在 ThreadLocal 中,这样就能保证处理的都是同一个连接。
1.2 Spring 事务处理
我们先大致了解一下 Spring 与事务有关的核心类,再接着分析 Spring 是如何接管事务的。
1.2.1 事务核心类
Spring 事务中涉及到的核心的类如下,下文如果不特别说明,都使用其简称,代表声明式事务中的默认实现类。
TransactionInterceptor:代理的核心类,也是事务处理的入口。
- PlatformTransactionManager:(最核心类)事务管理器,管理事务的各生命周期方法。简称 TxMgr,默认实现类为 DataSourceTransactionManager。
- TransactionAttribute:事务属性,包含隔离级别,传播行为,是否只读等信息。简称 TxAttr,通过 AnnotationTransactionAttributeSource 读取 @Transactional 注解中的事务属性。
- TransactionStatus:事务状态。简称 TxStatus,默认实现类 DefaultTransactionStatus,其中最核心的属性是事务对象 transaction(在 jdbc 中就是 ConnectionHolder)。
- TransactionInfo:事务信息,内含 TxMgr, TxAttr, TxStatus 等信息,同样保存在 ThreadLocal 中。简称 TxInfo。
- TransactionSynchronization:事务生命周期回调。简称 TxSync。
TransactionSynchronizationManager:事务状态管理器,实际上是使用 ThreadLocal 保存当前事物的状态信息。其中,jdbc 中最关键的 ConnectionHolder 信息就是保存在 resources 变量中。简称 TxSyncMgr。
1.2.2 事务启动
@EnableTransactionManagement 开启事务时,默认会通过 @Import 注册 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 两个配置类组件。
AutoProxyRegistrar:开启 AOP 注解驱动。实际上注册 InfrastructureAdvisorAutoProxyCreator 的 AOP 后置处理器。
- ProxyTransactionManagementConfiguration:事务核心配置类,注册与事务相关的组件。无论是 xml 还是注解驱动,最终都会注册这三个最核心的组件。
- AnnotationTransactionAttributeSource:读取 @Transactional 注解的属性 TxAttr。
- TransactionInterceptor:事务处理的核心入口。
- BeanFactoryTransactionAttributeSourceAdvisor:和 Spring AOP 的其它 Advisor(增强)一样,它有两个最主要属性 Advice(通知)和 Pointcut(切入点)。其中 Advice 就是 TransactionInterceptor,而 Pointcut 则是 TransactionAttributeSourcePointcut。这个切入点 Pointcut 也很简单,MethodMatcher 和 ClassFilter 的匹配,实际上都是委托给 TransactionAttributeSource 组件完成的。
总结:开启声明式事务后,AnnotationTransactionAttributeSource 会读取类或方法上 @Transactional 注解,决定是否生成 AOP 代理。其事务的核心处理由 TransactionInterceptor 这个拦截器完成,它也是事务处理的入口。
1.2.3 事务处理
有了上面的基础,我们再来看一下 Spring 是如何处理事务。 说明:我们已经说了,Spring 事务是典型的 AOP 应用。所有标注有 @Transactional 注解的类或方法都会被 AOP 代理,代理后的核心逻辑在 TransactionInterceptor 类中,大致可以分为准备事务(1 ~ 3)和执行事务(4)两部分。
- 读取事务属性(TxAttr):getTransactionAttributeSource 方法会从 AnnotationTransactionAttributeSource 读取 @Transactional 注解中的事务属性到 TransactionAttribute 中。
- 获取事务管理器(TxMgr):determineTransactionManager 方法根据 txAttr 属性配置获取事务管理器。如果 txAttr 配置了事务管理器就使用配置的,否则使用默认的事务管理器(从 beanFactory 容器中获取)。
- 获取事务对象(TxObject):createTransactionIfNecessary 内部调用 TxMgr#getTransaction(txAttr) 创建事务,对 jdbc 而言就是创建一个 connection。getTransaction 会根据事务的传播行为 propagationBehavior 决定是否创建新的事务。Spring 有 7 种事务的传播行为,默认是 REQUIRED,后面会具体分析。
执行事务:proceedWithInvocation 方法会执行业务逻辑。如果没有异常,则调用 commitTransactionAfterReturning -> TxMgr#commit(TxStatus) -> connection.commit() 提交事务,否则调用 completeTransactionAfterThrowing-> TxMgr#rollback(TxStatus) -> connection.rollback() 方法回滚事务。
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 1. 获取事务属性。
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 2. 根据事务属性获取事务管理器
final TransactionManager tm = determineTransactionManager(txAttr);
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 3. 事务处理
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 3.1 生成新的事务
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try
retVal = invocation.proceedWithInvocation(); // 3.2 执行业务代码
} catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex); // 3.3 事务回滚
throw ex;
} finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo); // 3.4 事务提交
return retVal;
}
...
}
总结:TransactionInterceptor 类封装了事务处理的核心逻辑,和原生的 jdbc 事务处理流程基本相同。其主要的功能基本上都委托给了事务管理器(TxMgr)。
2. 源码分析
2.1 事务状态管理器
TransactionSynchronizationManager 保存了事务的状态,这些状态都是通过 ThreadLocal 保存的。(可以暂时忽略,状态同步并不影响事务的执行)
actualTransactionActive:如是否有事务开启。
- currentTransactionIsolationLevel:事务的隔离级别。
- currentTransactionReadOnly:事务是否是只读。对于只读事务,数据库可以做一些优化。
- currentTransactionName:事务名称。
- synchronizations:事务生命周期回调接口。
- resources:其它绑定的资源,如 DataSourceTransactionManager 将连接 ConnectionHolder 就保存在该变量中。
如果是新建的事务,会通过 prepareSynchronization 将当前事务的状态信息保存到 ThreadLocal 中。
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
2.2 事务管理器
AbstractPlatformTransactionManager 是各种事务管理器的抽象基类,也可以说是骨架。它封装了很多事务管理的流程代码,如获取事务、事务的传播行为、事务的挂起和恢复、事务提交和回滚等待,具体的实现则需要子类完成。
- getTransaction(TransactionDefinition):根据事务配置 TxAttr 获取事务对象。对声明式事务而言,根据 @Transactional 注解配置获取事务。
- commit(TransactionStatus):提交事务模板方法,具体由子类完成。
- rollback(TransactionStatus):回滚事务。
下面列出一些主要的模板方法。
- doGetTransaction:用于从 TxMgr 中拿一个事务对象。如果当前已经有事务的话,返回的对象应该是要包含当前事务信息的。
- isExistingTransaction:用于判断一个事务对象是否对应于一个已经存在的事务。Spring会根据这个方法的返回值来分类讨论事务该如何传播。
- doBegin:物理开启事务。
- doSuspend:将当前事务资源挂起。对于我们常用的 DataSourceTransactionManager,它的资源就是ConnectionHolder。会将 ConnectionHolder 与当前线程脱钩并取出来。
- doResume:恢复当前事务资源。对于我们常用的 DataSourceTransactionManager,它会将 ConnectionHolder 重新绑定到线程上。
- doCommit:物理提交事务。
- doRollback:物理回滚事务。
doSetRollbackOnly:给事务标记为回滚。对于我们常用的 DataSourceTransactionManager,它的实现是拿出事务对象中的 ConnectionHolder 打上回滚标记。这个标记是一种 “全局的标记”,因为隶属于同一个物理事务都能读到同一个 ConnectionHolder。
2.2.1 获取事务入口
核心方法是 TxMgr#getTransaction(TxAttr)。上文已经分析了 TransactionInterceptor 拦截事务请求时,会通过 createTransactionIfNecessary 方法创建事务,实际上就是调用 TxMgr#getTransaction(TxAttr) 获取事务。
getTransaction 方法获取事务的逻辑比较复杂,因为要考虑事务的传播行为,如果事务已经存在,就需要考虑是否通过 suspend 和 resume 方法挂起和恢复事务。涉及到的方法:getTransaction:根据事务配置 TxAttr 获取事务。其中 doGetTransaction 方法用于获取当前的事务对象。
- suspend:挂起当前事务,返回当前事务的所有状态。主要包含两点,一是事务的状态,指的是 TxSyncMgr 中的状态信息;二是事务对象本身,由具体子类的 doSuspend 实现。最后返回的 SuspendedResourcesHolder 包含事务状态和事务对象,缓存到新的事务中。
- resume:恢复当前事务,包括事务状态和事务对象。将 SuspendedResourcesHolder 中的事务状态信息恢复到 TxSyncMgr 状态中,最后子类通过 doResume 方法将事务对象 ConnectionHolder 重新绑定到线程上。
本小节以 Spring 默认的 REQUIRED 传播行为(如果当前有事务就加入到当前事务中,否则就创建一个新的事务)进行分析。
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
Object transaction = doGetTransaction();
...
// 1. 当前事务存在
if (isExistingTransaction(transaction)) {
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 2. 当前事务不存在
// 2.1 MANDATORY 必须在事务中执行,如果事务不存在则抛出异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException();
// 2.2 REQUIRED、REQUIRES_NEW、NESTED 创建新的事务执行
} else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 所谓的挂起,实际上是将当前事务的状态保存到新事务的suspendedResources字段中
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
def, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, def);
prepareSynchronization(status, def);
return status;
} catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
// 2.3 SUPPORTS、NOT_SUPPORTED、NEVER 不在事务中执行
} else {
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
说明:获取事务时,通过 doGetTransaction 获取当前事务 transaction,然后针是否存在事务分别处理。我们先只分析当前事务不存在的情况,如果是事务传播行为是 REQUIRED(必须在事务中执行),则需要创建新的事务。此时 suspend 参数为 null,也就是不会调用 doSuspend 方法解绑连接,事务仍会在当前连接上提交,但会创建一个新的事务。
2.2.2 获取事务的步骤
- 第一步,调用 suspend 方法挂起当前事务。也就是将当前事务的状态和事务对象封装到 suspendedResources 对象中,最后保存到当前事务对象 status 中。
- 如果当前事务不存在,suspend 不会调用 doSuspend 方法。
- 如果当前事务存在,suspend 会调用 doSuspend 获取当前的事务对象。DataSourceTxMgr#doSuspend 会将事务对象置空,同时解绑 ThreadLocal。同时 doBegin 方法发现没有事务对象,则创建一个新的连接,并绑定到 ThreadLocal 中。
- 第二步,调用 doBegin 配置事务对象。对于 DataSourceTxMgr#doBegin 方法,一是判断连接是否存在,如果存在就创建一个新的连接,并绑定到 ThreadLocal。二是配置连接,如只读、事务隔离级别等等。
- 第三步,调用 prepareTransactionStatus 激活事务状态管理器。TxSyncMgr 维护了多个 ThreadLocal 变量来保存当前事务的状态信息,prepareTransactionStatus 方法将当前事务的状态绑定到 ThreadLocal。
2.2.3 事务的传播行为
Spring 中的 Propagation 枚举和 TransactionDefinition 接口定义了 7 种事务传播行为。
传播行为 | 存在事务 | 不存在事务 |
---|---|---|
REQUIRED | 加入当前事务 | 创建新的事务 |
REQUIRES_NEW | 挂起当前事务,创建新的事务 | 创建新的事务 |
SUPPORTS | 加入当前事务 | 不在事务中执行 |
NOT_SUPPORTED | 挂起当前事务,不在事务中执行 | 不在事务中执行 |
MANDATORY | 加入当前事务 | 抛出异常 |
NEVER | 抛出异常 | 不在事务中执行 |
NESTED | 创建嵌套事务 | 创建新的事务 |
关于事务的传播行为的代码见 AbstractPlatformTxMgr#getTransaction,之前我们已经分析了事务不存在时,事务的创建逻辑,下面再看一下事务存在时的处理逻辑。
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// 1. NEVER:不能存在事务,如果有事务则抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException();
}
// 2. NOT_SUPPORTED:挂起当前事务,不在事务中执行
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
// 3. REQUIRES_NEW:挂起当前事务,创建新的事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
} catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 4. NESTED:创建嵌套事务???
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
...
}
// 5. SUPPORTS、REQUIRED:在当前事务中执行
if (isValidateExistingTransaction()) {
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
说明:如果事务需要挂起,则调用 suspend(transaction) 将当前事务的状态缓存起来,同时将当前事务的状态全部重置。当事务结束时,则调用 resume 恢复事物的状态。
2.2.4 事务的挂起和恢复
事务的挂起和恢复本质是将事务的状态和事务对象封装成 SuspendedResourcesHolder,保存到当前事务中。当事务执行结束后,不管是回滚还是提交事务,都需要恢复原先的事务。
(1)事务挂起
// suspend 方法将当前事务的状态和事务对象封装到SuspendedResourcesHolder中
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
suspendedResources = doSuspend(transaction);
}
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
} catch (RuntimeException | Error ex) {
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
} else if (transaction != null) {
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
} else {
return null;
}
}
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
说明:suspend 方法将当前事务的状态(TxSyncMgr)和事务对象(doSuspend)封装到 SuspendedResourcesHolder 中。另外,当抛出 RuntimeException 和 Error 异常时会回滚。
(2)事务恢复
protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
doResume(transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}
2.2.5 事务的提交和回滚
commit 和 rollback 分别提交和回滚事务,它们处理的逻辑都差不多:
- 事务提交或回滚时,大致可以分为以下三种情况。
- 已经设置保存点:(嵌套事务)回滚到 savepoint。
- 外层事务:(正常情况)直接提交或回滚事务。
- 已经设置回滚标记:(内存事务发生异常时,会设置回滚标记)如果 failEarlyOnGlobalRollbackOnly = true 则会立即抛出异常,默认为 false。
- 异常处理:如果是 RuntimeException 或 Error 则需要调用 doRollbackOnCommitException 处理异常。如果是外层事务则直接回滚,否则设置回滚标记 rollbackOnly=true。
- 状态恢复:cleanupAfterCompletion(status) 方法恢复被挂起事务的状态,总之所有的状态都要回滚到事务开始前。
(1)事务提交
@Override
public final void commit(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 1. 设置单个事务的回滚状态,相当于局部状态。rollbackOnly=true 只能回滚
if (defStatus.isLocalRollbackOnly()) {
processRollback(defStatus, false);
return;
}
// 2. 设置ConnectionHolder的回滚状态,相当于全局状态
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
processRollback(defStatus, true);
return;
}
processCommit(defStatus);
}
// triggerXX 方法都是TxSync生命周期的回调。
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
// 1.1 提交事务,有savepoint就回滚到保存点(嵌套事务)
if (status.hasSavepoint()) {
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
// 1.2 (核心逻辑)如果是外层事务,则直接提交事务
} else if (status.isNewTransaction()) {
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
// 1.3 如果有异常,设置了全局回滚,则直接抛出异常
} else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
if (unexpectedRollback) {
throw new UnexpectedRollbackException();
}
} catch (UnexpectedRollbackException ex) {
// 2.1 未知异常,直接抛出
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
} catch (TransactionException ex) {
// 2.2 TransactionException异常,默认直接抛出异常
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex); // 设置全局回滚
} else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
} catch (RuntimeException | Error ex) {
// 2.3 Spring默认这两种异常回滚
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex); // 设置全局回滚
throw ex;
}
try {
triggerAfterCommit(status);
} finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
} finally {
// 3. 恢复挂起的事务状态及事务对象,总之所有的状态都要回滚到事务开始前的状态
cleanupAfterCompletion(status);
}
}
说明:commit 提交时,事务处理如下。
- 回滚标记:通过内层事务异常时,设置了回滚标记,直接调用 processRollback 进行回滚。一切正常,才调用 processCommit 进行提交。
- 正常情况:外层事务直接提交。内层事务如果允许快速失败(failfast),则直接抛出异常。
- 异常情况:提交时发生异常,这里的异常不仅仅是数据库事务提交异常,也包括事务生命周期回调异常的场景,此时调用 doRollbackOnCommitException 处理回滚。外层事务则直接回滚。内层事务设置回滚标记, 让外层事务回滚。
补充:isLocalRollbackOnly 和 isGlobalRollbackOnly 的区别?
- isLocalRollbackOnly:设置单个事务的 status.rollbackOnly,只有单个事务可见。
- isGlobalRollbackOnly:设置的是连接的 connectionHolder.rollbackOnly,即该连接的所有事务都可见,所以是全局的。在 Spring 中,doRollbackOnCommitException 和 doSetRollbackOnly 方法都是设置的全局回滚异常。
(2)事务回滚
@Override
public final void rollback(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processRollback(defStatus, false);
}
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
// 1.1 嵌套事务,直接回滚到savepoint
if (status.hasSavepoint()) {
status.rollbackToHeldSavepoint();
// 1.2 (核心逻辑)外层事务,直接回滚
} else if (status.isNewTransaction()) {
doRollback(status);
// 1.3 内层事务,设置全局回滚或直接抛出异常
} else {
// 设置全局回滚
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
doSetRollbackOnly(status);
}
}
// 默认failEarlyOnGlobalRollbackOnly=false,也就是不允许提前抛出异常
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false; // 不允许提前抛出异常
}
}
} catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
if (unexpectedRollback) {
throw new UnexpectedRollbackException();
}
} finally {
cleanupAfterCompletion(status);
}
}
说明:rollback 提交时,事务处理如下。
- 正常情况:外层事务直接回滚。内层事务调用 doSetRollbackOnly 方法设置回滚标记。
- 异常情况:回滚时发生异常,直接抛出异常。
2.3 JdbcTemplate 事务处理
JdbcTemplate 如果开启了事务,TxManager#getTransaction 获取事务时,会将从连接池 DataSource 中获取的连接 ConnectionHolder 绑定到 TxSynchMgr.resources 变量中,resources 是 ThreadLocal,通过 ThreadLocal 做线程隔离。同时 JdbcTemplate 也是这个 ThreadLocal 中获取连接 ConnectionHolder。这样 Spring 就接管了整个事务。 说明:上述时序图主要关注 ConnectionHolder 获取的连接过程,通过 ThreadLocal 变量保证事务和业务处理的连接是同一个。另外,并不是所有的事务处理都会完整的经历上述时序图的所有过程。比如,
- 如果直接在当前事务中执行,就不需要调用 suspend 来挂起当前事务。这样 doBegin 时,就不需要调用 dataSouce.getConnection() 来重新获取连接。
如果当前事务不存在,调用 suspend 时就不需要调用 doSuspend 来解绑事务。同样也不需要重新获取连接。
3. 总结时刻
推荐阅读
《Spring事务源码阅读笔记》:推荐指数五星。对 Spring 事务分析比较全面。
- 《read-only=”true” 只读事务》:只读事务
每天用心记录一点点。内容也许不重要,但习惯很重要!