概述
spring-tx 模块为 Spring 框架提供了强大而灵活的事务管理功能。在这个系列的文章中,我们将深入源码探讨 Spring 框架事务部分的内部工作原理和关键组件。
一般来说,当我们使用 Spring 事务的功能时,实际上分为两种用法:
- 编程式事务:即通过
TransactionTemplate显式的在代码中声明事务; - 声明式事务:即使用
@Transcational注解将方法操作声明在事务中,是最常用的手段;
不过,虽然两者的生效方式方式不同——前者依靠具体的组件,后者依靠 AOP 代理增强——但是实际上都是基于事务管理 TranscationManager 实现。
在开始研究 Spring 事务机制之前,我们需要先对该模块中的一些基础组件和概念有一个基本的了解。
1.基本概念
1.1.事务的定义
TransactionDefinition 人如其名,它表示某种事务的定义,它是规定了一个事务包括名称,传播级别,或者是否只读等在内的各种属性:
public interface TransactionDefinition {
// 八种传播行为
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
// 四种隔离级别
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
// 获取传播行为
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}
// 获取隔离级别
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}
// 获取事务超时时间
default int getTimeout() {
return TIMEOUT_DEFAULT;
}
// 是否只读事务
default boolean isReadOnly() {
return false;
}
// 获取事务名称
@Nullable
default String getName() {
return null;
}
}
它有一个默认的实现类 DefaultTransactionDefinition ,是比较常用的实现。
1.2.事务状态

TransactionStatus 是一个表示事务状态的对象,通过修改它,我们允许指定在完成操作后,是否要提交或者回滚事务:
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
// 是否有保存点
boolean hasSavepoint();
// 刷新状态,该方法来自于 Flushable
@Override
void flush();
}
它实现了三个接口,其中,我们关注 TransactionExecution 和 SavepointManager :
// 表示一个正在执行的事务
public interface TransactionExecution {
// 是否是新事务
boolean isNewTransaction();
// 将当前事务状态设置为回滚
void setRollbackOnly();
// 当前事务是否需要回滚
boolean isRollbackOnly();
// 当前事务是否已经提交或者
boolean isCompleted();
}
// 表示事务支持操作保存点
// 在事务处理中,Savepoint(保存点)是一种用于在事务内部划分和管理的逻辑标记点。它允许在事务进行中创建一个可回滚的保存点,并在后续的操作中选择性地回滚到该保存点,而不必回滚整个事务。
// 该功能与事务本身一样,需要底层的数据库提供支持
public interface SavepointManager {
// 创建一个保存点
Object createSavepoint() throws TransactionException;
// 回滚到指定的保存点
void rollbackToSavepoint(Object savepoint) throws TransactionException;
// 释放指定的保存点
void releaseSavepoint(Object savepoint) throws TransactionException;
}
TransactionStatus 是一次事务操作中真正承载“状态”的对象,从这个角度上看,它即一个正在执行的事务本身。
2.事务管理器
2.1.平台事务管理器
在上文,我们提到了“事务管理器”,即 TransactionManager,它是一个空接口,实际真正有用的是它的子接口平台事务管理器 PlatformTransactionManager。
public interface PlatformTransactionManager extends TransactionManager {
// 获取一个事务,隔离级别就生效在这里
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
// 提交事务
void commit(TransactionStatus status) throws TransactionException;
// 回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
它是 Spring 提供的一个对接数据库框架与数据库的中间层,第三方的框架通过实现 PlatformTransactionManager 来将自己的事务功能接入 Spring。而我们则可以通过它在对底层无感知的情况下轻松的完成对事物的各种操作。
这里举两个常见的例子:
DataSourceTransactionManager:默认的事务管理器,只要基于 Java 的DataSource访问数据库,就都可以用它;JpaTransactionManager:JPA 所使用的事务管理器,如果你使用了 JPA,就会用它作为事务管理器;
此外,除了 TranscationTemplate 依赖它实现以外,声明式事务的核心 TransactionInterceptor 也依赖它实现。
2.2.运行机制
从上述接口中的抽象方法,其实我们就可以大概猜到它的运行原理了:
- 向管理器传入一个事务定义对象
TransactionDefinition,管理器将会开启一个事务,并返回TransactionStatus对象; - 我们可以通过
TransactionStatus对象对这个已经开启的事务进行各种操作,比如设置为回滚,或者开启保存点以启动一个嵌套事务等; - 当完成了各种事务操作后,我们再将
TransactionStatus对象传入管理器,以将事务真正的提交或者回滚。
如此,我们就将 TransactionDefinition、TransactionStatus 与 TransactionManager 三个重要的概念串联起来了。
3.事务资源
3.1.事务上下文
在开始前,我们需要着重讲一下 TransactionSynchronizationManager 这个类,它在内部持有多个 ThreadLocal ,用于保存有一次请求中所有关联的资源,是实际意义上的“上下文”对象:
public abstract class TransactionSynchronizationManager {
// 用于存放当前事务的“资源”,一般有两种情况:
// 1、DataSource 对象为 key,ConnectionHolder 对象为 value
// 2、SqlsessionFactory 对象为 Key,SqlSessionHolder 对象为 value
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
// 在本次请求中等待在各个关键节点执行的回调接口
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
// 当前事务的名称
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
// 当前事务是否为只读的
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");
// 当前事务的隔离级别
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");
// 当前事务是否已经开启
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");
}
它用六个 ThreadLocal 存放了当前线程中,可能存在的横跨了多个方法调用的事务信息。后续当我们说起“当前事务”时,指的就是 ThreadLocal 里面存放的事务信息。
或许是出于要在多个线程中与被多个组件进行操作,因此它没有被作为一个单例 Bean 被注入到各个组件中使用,而是作为一个全局的静态单例,让各个组件通过它提供的静态方法去操作里面的上下文信息。
3.2.事务资源
在 TransactionSynchronizationManager 中存在名为 resources 的成员变量,这个 ThreadLocal 存放了当前事务涉及到的“资源”。
它们实际上就是 Connection 或者 SqlSession 这类真正用于操作事务(或者说构成事务?)的东西,为了同时兼容两类键值,它的泛型都是 Object:
- 当使用
DataSourceTransactionManager的时候:DataSource对象为 key,ConnectionHolder对象为 value; - 当使用
HibernateTransactionManager的时候:SqlsessionFactory对象为 Key,SqlSessionHolder对象为 value
光看这个成员变量,我们就能提前明白两件事:
- 由于它存放的是以数据源为 key 的
Map集合,这意味着只要在同一线程中,即使是多数据源操作,甚至在操作中指定了不同的事务管理器,都是可以被一并管理的; - 由于它使用
ThreadLocal,因此当新事务在另一个线程中进行时,已有的事务就没法正常的传播;
3.3.事务同步器
除了关于事务的一些信息,我们还会注意到它为每个线程保存了一个 Set<TransactionSynchronization> 集合,里面的 TransactionSynchronization 实际上就是一个类似于 PostProcessor 这样的回调接口,用于感知并在事务的生命周期中的特定节点触发回调操作:
public interface TransactionSynchronization extends Flushable {
/** Completion status in case of proper commit. */
int STATUS_COMMITTED = 0;
/** Completion status in case of proper rollback. */
int STATUS_ROLLED_BACK = 1;
/** Completion status in case of heuristic mixed completion or system errors. */
int STATUS_UNKNOWN = 2;
// 当被挂起时触发,用于将资源从当前上下文解绑
default void suspend() {
}
// 从挂起状态回复时触发,用于将解绑的资源重新绑定回上下文
default void resume() {
}
// 释放链接时触发
@Override
default void flush() {
}
// 事务提交前触发
default void beforeCommit(boolean readOnly) {
}
// 事务完成前(即提交或者回滚后)触发
default void beforeCompletion() {
}
// 事务提交后触发
default void afterCommit() {
}
// 事务完成后(即提交或者回滚后)触发
default void afterCompletion(int status) {
}
}
事务中的同步操作总是与当前事务绑定,因此当出现诸如”当前事务被挂起“,或者”另外开启一个事务“的这种场景,新的事务中总是会先暂时移除已有的同步操作,然后等到当前事务提交了,再回头去把移除的事务重新放回上下文。
我们会在事务管理器中看到大量的 xxxTransactionSynchronization 格式的方法,它们都是由于管理回调的。
总结
事务的定义与操作
Spring 事务体系中,最基础的两个抽象分别是:
TransactionDefinition:事务定义接口,该接口规定了 Spring 的事务可以设置包括是否只读、传播行为与超时时间等属性,TranscationTemplate与声明式事务中的TransactionAttribute都实现/继承了该接口,它是事务的模具。TranscationStatus:事务状态接口,它是TranscationExecution的下级接口,表示一个正在进行的事务,你可通过它完成创建保存点、提交或者回滚等操作,它是事务本身;
事务管理器
在此基础上,Spring 又提供了事务管理器 TransactionManager,它用于屏蔽各种复杂的底层事务管理机制,从而允许我们直接通过传入一个 TransactionDefinition 获取一个 TranscationStatus,进而在对底层无感知的情况下轻松对事务进行各种操作。
TransactionManager 的提供了一个通用的模板类 PlatformTransactionManager,我们最常打交道的 DataSourceTransactionManager(Mybatis 与 JdbcTemplate 的事务管理器)和 JpaTransactionManager (JPA 的事务管理器)都基于它实现。
事务上下文
当我们发起一个方法调用时,可能会涉及到一个非常长且嵌套层级非常深的调用流程,而事务要在调用栈中正确的传播,就需要一个贯穿全局的上下文,这就是事务上下文 TransactionSynchronizationManager。
它是一个静态的工具类,它在内部维护了数个 NamedThreadLocal 类(本质还是一个 ThreadLocal),当开启一个事务后,涉及到的各种事务资源(比如数据源或者 SqlSession)、事务配置(比如当前事务的隔离级别、是否只读等属性)还有回调接口(比如 TransactionSynchronization)都线程为单位保存在 NamedThreadLocal 里面。因此,只要在调用链都在一个线程中,那么这些事务信息就可以正确的传递。