逃离森林
37.60M · 2026-02-26
上篇文章介绍了 Spring AOP 的实现原理,掌握了 AOP,再理解 Spring 事务会简单很多,值得注意的是,提到事务,我们通常说的是关系型数据库的事务,Spring 只是一个 Java 开源框架,它不存在事务的说法,Spring 是通过一系列代码逻辑来管理数据库事务,实现业务场景中事务的嵌套处理。本篇文章先介绍 Spring 事务管理涉及的相关类
本篇文章使用的 SpringBoot 版本是 3.4.1 ,对应 Spring 版本 6.2.1。
这里我以 SpringBoot 源码入口为起点,画了一个相关的流程图,包含了 SpringBoot、Spring 事务、Spring AOP、Spring 事件、BeanFactoryPostProcessor、BeanPostProcessor 等所有 Spring 知识,以及相关模块之间的交互联系,后续也会持续更新此图(因为我自己还没有学完),我试了下作者侧这边更新后,分享的协作链接也会实时变更,希望对大家有帮助
SpringBoot & Spring 架构图 持续更新 对于即将需要面试的同学应该会比较有帮助!
直接在方法上标注 @Transactional 即可,这样 test1() 方法就会以存在事务的方式运行,抛出相关异常会回滚事务
@Transactional
public void test1(){}
@Transactional 内部有很多属性用来控制事务的行为
public @interface Transactional {
//指定事务管理器
@AliasFor("value")
String transactionManager() default "";
//传播行为
Propagation propagation() default Propagation.REQUIRED;
//指定哪些异常要回滚
Class<? extends Throwable>[] rollbackFor() default {};
//指定超时时间
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
//...
}
包括选择事务管理器、指定传播行为、要回滚的异常、超时时间等等。
TransactionTemplate 是 spring-tx 模块给我们提供的一个操作事务的类,针对有返回值和无返回值提供了两种 API,在 SpringBoot 中提供了自动配置,直接使用即可
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
public static class TransactionTemplateConfiguration {
@Bean
@ConditionalOnMissingBean(TransactionOperations.class)
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
return new TransactionTemplate(transactionManager);
}
}
@Autowired
private TransactionTemplate transactionTemplate;
public void test2(){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
//业务行为
} catch (Exception e){
status.setRollbackOnly();
throw new RuntimeException("创建订单失败", e);
}
}
});
}
当业务操作需要结果时可以使用
public void test3(){
Object obj = transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
try {
//业务操作
return null;
} catch (Exception e){
// 标记事务为回滚
status.setRollbackOnly();
throw new RuntimeException("创建用户失败,事务已回滚", e);
}
}
});
}
编程式事务可以和声明式事务结合使用,TransactionTemplate 的默认传播行为是 REQUIRED,这意味着没有外层声明式事务的影响下,每一次 transactionTemplate.execute() 都是一个新事物,使用编程式事务让我们可以 更灵活的控制事务粒度。
Spring 事务管理提供了以下几种传播行为,每一种传播行为会对当前事务的嵌套有不同的处理逻辑。
| 传播行为 Propagation | 翻译 | 如果当前有事务 | 如果当前无事务 | 异常回滚影响 | 典型应用场景 |
|---|---|---|---|---|---|
| REQUIRED | 必需(默认) | 加入当前事务 | 创建新事务 | 全部回滚 | 大多数业务方法 |
| SUPPORTS | 支持 | 加入当前事务 | 非事务运行 | 看情况 | 查询方法,可事务可非事务 |
| MANDATORY | 强制 | 加入当前事务 | 抛出异常 | 全部回滚 | 必须被事务方法调用 |
| REQUIRES_NEW | 新建事务 | 挂起当前事务,创建新事务 | 创建新事务 | 各自独立回滚 | 独立业务,如日志记录 |
| NOT_SUPPORTED | 不支持 | 挂起当前事务,非事务运行 | 非事务运行 | 不影响事务 | 非核心业务,避免事务锁 |
| NEVER | 从不 | 抛出异常 | 非事务运行 | 不适用 | 强制非事务环境 |
| NESTED | 嵌套 | 创建嵌套子事务(保存点) | 创建新事务 | 子事务回滚不影响主事务 | 部分失败不影响整体的场景 |
Spring 注解事务的核心就是对不同的传播行为或者说对事务嵌套进行处理,因为关系型数据库 MySQL 是不支持事务嵌套的,假如我们在 MySQL 客户端开启事务之后,再次执行 Begin,那么它会先提交当前事务然后重新开启新事务
select * from Cash_repay_apply
BEGIN;
update Cash_repay_apply set repay_status = 'TEST' where id = 1
BEGIN; -- 会先提交事务,再开启新事务
update Cash_repay_apply set repay_status = 'TEST' where id = 2
COMMIT;
上述代码无法嵌套 BEGIN,执行第二个 BEGIN 时,会先提交上一次 BEGIN 的事务。
事务管理器 TransactionManager 是 Spring 操作数据库事务的核心顶层接口,它的子接口 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;
}
无论应用程序玩的有多花哨,最终无非是对数据库事务进行操作,那么数据库事务其实就三个点,开启事务、提交、回滚。所以事务管理器提供了三个方法来对应
它把事务定义封装到 TransactionDefinition 中,根据它获取一个数据库事务对象,事务结果信息封装到 TransactionStatus 对象中,然后提供了提交事务和回滚事务两个方法由具体子类实现。在 SpringBoot 项目中通常我们会引入 spring-boot-starter-jdbc
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
引入之后会启用 JdbcTransactionManagerConfiguration,这时候具体的实现就是 JdbcTransactionManager 。不过有意思的是具体的实现在抽象事物管理器 AbstractPlatformTransactionManager 中,因为这一套开启事务、提交、回滚的操作其实都是通用的,无非就是用 jdbc 驱动的 Connection 对象来和 MySQL 通信,执行客户端命令罢了
回想我们之前学习的 jdbc 知识,事务的操作一定离不开 jdbc 规范,我们跟踪源码深入会发现,事务的获取、提交、回滚的确都是通过 Connection 对象来操作的
public interface Connection extends Wrapper, AutoCloseable {
void commit() throws SQLException;
void rollback() throws SQLException;
}
SpringBoot 默认数据库连接池是 HikariCP 。它的 Connection 实现类是 HikariProxyConnection,不过实际上它几乎啥也没干,包装了一层,最后还是调用 jdbc 驱动的 Connection 实现类 ConnectionImpl 。
从这个类的名字可以看出,它是事务的定义抽象,结合事务管理器 PlatformTransactionManager.getTransaction(TransactionDefinition definition) 的入参我们可以知道 TransactionDefinition 定义了事务的相关属性,根据它的定义通过事务管理器获取一个数据库事务。
在设计上源码中抽象出了几个继承关系如下图 。使用声明式事务时候最终实现类是 RuleBasedTransactionAttribute 公共属性的定义基本上还是在 DefaultTransactionDefinition 中
TransactionStatus 描述了事务开启后运行过程中状态。默认实现类是 DefaultTransactionStatus,结合事务管理器的 commit(TransactionStatus status) 和 rollback(TransactionStatus status) 的方法入参可以看出,TransactionStatus 封装了最终的事务结果(提交/回滚),提交到数据库中。
public class DefaultTransactionStatus extends AbstractTransactionStatus {
private final String transactionName; // 事务名称
private final Object transaction; // 底层事务对象
private final boolean newTransaction; // 是否是新事务(通常可以理解为是否是当前事务方法自己创建的事务)
private final boolean newSynchronization; // 是否新的事务同步(同上)
private final boolean nested; // 是否是嵌套事务
private final boolean readOnly; // 是否只读
private final Object suspendedResources; // 当前事务方法挂起的事务资源
}
了解了 PlatformTransactionManager、TransactionDefinition、TransactionStatus,
结合 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 对事务的管理,本质上是对 Connection 进行管理,就是调用这三个方法来管理 Connection 对象。
下面我们用表格的方式对比一下这两个类·
| 维度 | TransactionDefinition | TransactionStatus |
|---|---|---|
| 角色 | 事务的定义(配置时) | 事务的状态(运行时) |
| 生命周期 | 事务 开始前 定义,全局共享 | 事务 开始后 创建,事务结束销毁 |
| 可变性 | 事务获取后 不可变(配置固定) | 可变(状态会随业务代码变化) |
| 存储内容 | 事务的配置属性 | 当前事务的运行状态 |
| 创建时机 | 在获取事务前由调用者提供 | 事务开始时由事务管理器创建 |
这里我们举几个常见的事务场景
@Transactional
public void test(){
//...db 操作
}
例如这段简单的代码,事务的开启流程图如下
其实就是代理方法用 AOP 实现了一个环绕通知的效果,在目标方法执行前开启事务,在目标方法执行后提交或者回滚事务,我们暂时可以不关注开启事务和回滚事务内部还有哪些细节,后面会详细介绍。
以下面这段嵌套事务的代码为例
@Transactional
public void test(){
//db操作
test2Service.testNewTx();
}
@Service
public class Test2Service{
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testNewTx(){
//db 操作...
}
}
这段示例代码是一个已存在的事务中,嵌套了一个需要新开事务的业务方法,图示如下
这里代码示例中,会先开启 test() 方法的事务,然后执行到 testNewTx() 时,发现当前方法传播行为是新开事务,于是会再开启一个事务,等内层方法 testNewTx() 结束后,提交/回滚它的事务,然后再继续进行外层 test() 方法的事务,从这个案例不难想象,无论多少层事务嵌套,都是一样的处理逻辑,无非是嵌套层数的多少罢了,后面会详细介绍源码,实现相对比较简单
Spring 事务管理是通过代理目标对象,执行代理方法,在原方法执行前开启事务、原方法执行后提交/回滚事务,当遇到嵌套事务时逻辑亦然
上一篇 AOP 实现原理,我们知道执行代理对象方法会调用一系列拦截器实现拦截。上一段我们说了 Spring 管理事务实际上就是对目标对象代理,实现了一个环绕通知的效果,而 TransactionInterceptor 就是实现这个环绕通知包装的拦截器。
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
//......
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
//以事务方式调用
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
}
观察源码可以发现 invokeWithinTransaction() 是父类 TransactionAspectSupport 实现的。具体实现逻辑后面会介绍
TransactionSynchronizationManager 是事务管理过程中一个非常核心的类,它的作用是保存事务管理过程中事务的相关信息,绑定到当前线程,包括 事务资源、同步器、隔离级别、事务激活状态 等
public abstract class TransactionSynchronizationManager {
//保存当前线程的事务资源,key 通常是数据源,value 通常是 Connection 包装对象
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 存储事务相关信息。其核心方法如下
//绑定资源(把修改后的 Connection 对象绑定到当前线程)
public static void bindResource(Object key, Object value){}
//解绑资源(把修改后的 Connection 对象从当前线程解绑)
public static Object unbindResource(Object key){}
//注册同步器(注册事务阶段回调代码,下一段会介绍 TransactionSynchronization)
public static void registerSynchronization(TransactionSynchronization synchronization){}
//获取同步器
public static List<TransactionSynchronization> getSynchronizations(){}
查看注释,TransactionSynchronization 是事务同步回调接口,在 Spring 管理事务的过程中,事务管理进行到不同的阶段会回调不同的方法
public interface TransactionSynchronization extends Ordered, Flushable {
/**
* 挂起事务时回调
*/
default void suspend() {}
/**
* 恢复事务时回调
*/
default void resume() {}
/**
* Flush 的时候回调,例如 Hibernate/JPA 的 flush 操作.
*/
@Override
default void flush() {}
/**
* 创建新保存点后调用
*/
default void savepoint(Object savepoint) {}
/**
* 回滚到上一个保存点前调用
*/
default void savepointRollback(Object savepoint) {}
/**
* 事务提交前调用
*/
default void beforeCommit(boolean readOnly) {}
/**
* 事务完成(回滚/提交)前调用
*/
default void beforeCompletion() {}
/**
* 事务提交后调用
*/
default void afterCommit() {}
/**
* 事务完成(提交、回滚)后调用
*/
default void afterCompletion(int status) {}
}
上一段我们介绍了 TransactionSynchronizationManager 中有一个 registerSynchronization() 方法可以注册事务同步回调接口,它是绑定到当前线程的,不需要交给 Spring 管理,例如
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void beforeCommit(boolean readOnly) {
System.out.println("TransactionSynchronization beforeCommit");
}
//...
});
值得一提的是 savepoint ,叫做保存点,是关系型数据库中支持部分回滚的一个命令
BEGIN;
update 语句1
update 语句2
SAVEPOINT test1;
update 语句3
update 语句4
ROLLBACK TO SAVEPOINT test1;
-- ......
使用 ROLLBACK TO SAVEPOINT 可以指定事务回滚到哪一个节点位置。在 Spring 项目中可以使用如下的方式来设置保存点
@Transactional
public void test1(){
CashRepayApply apply = cashRepayApplyMapper.getForUpdate(1L);
apply.setRepayStatus("test");;
cashRepayApplyMapper.updateById(apply);
//创建保存点
Object sp1 = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
apply.setRepayStatus("test2");
cashRepayApplyMapper.updateById(apply);
try {
if(true){
throw new RuntimeException("<UNK>");
}
} catch (Exception e){
//回滚到保存点
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(sp1);
}
}
这个类是事务执行器,它是 Spring 6.1 版本新增的功能,它的功能和 TransactionSynchronization 有些类似,也是在事务的不同阶段执行一些回调方法
public interface TransactionExecutionListener {
/**
* 事务开始前执行
*/
default void beforeBegin(TransactionExecution transaction) {}
/**
* 事务开始后执行
*/
default void afterBegin(TransactionExecution transaction, @Nullable Throwable beginFailure) {}
/**
* 事务提交前执行
*/
default void beforeCommit(TransactionExecution transaction) {}
/**
* 事务提交后执行
*/
default void afterCommit(TransactionExecution transaction, @Nullable Throwable commitFailure) {}
/**
* 事务回滚前执行
*/
default void beforeRollback(TransactionExecution transaction) {}
/**
* 事务回滚后执行
*/
default void afterRollback(TransactionExecution transaction, @Nullable Throwable rollbackFailure) {}
}
值得注意的是 TransactionExecutionListener 是事务管理器 AbstractPlatformTransactionManager 中的一个成员变量,
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager {
private Collection<TransactionExecutionListener> transactionExecutionListeners = new ArrayList<>();
//......
前面介绍过,事务的三个重要操作,开启、提交、回滚都是事务管理器来实现的,在这些操作的过程中,不同的阶段会遍历调用 TransactionExecutionListener 的不同方法。
和 TransactionSynchronization 最重要的一个区别是 TransactionSynchronization 是绑定到某一个事务(线程)上的回调,而 TransactionExecutionListener 是针对所有事务的回调。结合 TransactionExecutionListener 是抽象事务管理器的成员变量来思考,所以很明显 TransactionExecutionListener 是交给 Spring 管理才会被赋值到抽象事务管理器中生效的
在 SpringBoot 中, TransactionManagerCustomizationAutoConfiguration 自动配置类中
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfiguration(before = TransactionAutoConfiguration.class)
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionManagerCustomizationAutoConfiguration {
/*
* 从容器中获取 TransactionManagerCustomizer 的 Bean 列表,得到 TransactionManagerCustomizers
*/
@Bean
@ConditionalOnMissingBean
TransactionManagerCustomizers platformTransactionManagerCustomizers(
ObjectProvider<TransactionManagerCustomizer<?>> customizers) {
return TransactionManagerCustomizers.of(customizers.orderedStream().toList());
}
/*
* 从容器中获取 TransactionExecutionListener 的 Bean 列表,得到 ExecutionListenersTransactionManagerCustomizer
*/
@Bean
ExecutionListenersTransactionManagerCustomizer transactionExecutionListeners(
ObjectProvider<TransactionExecutionListener> listeners) {
return new ExecutionListenersTransactionManagerCustomizer(listeners.orderedStream().toList());
}
}
前面的文章介绍过 SpringBoot 源码中,XxxCustomizer 都是一些用户自定义扩展的实现抽象,TransactionManagerCustomizer 很明显是自定义事务管理器配置的,然后我们再看 JdbcTransactionManagerConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(DataSource.class)
static class JdbcTransactionManagerConfiguration {
/*
* 从容器中获取 TransactionManagerCustomizers 列表应用到 DataSourceTransactionManager 中
*/
@Bean
@ConditionalOnMissingBean(TransactionManager.class)
DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
DataSourceTransactionManager transactionManager = createTransactionManager(environment, dataSource);
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
/*
* 具体的事务管理器
*/
private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) {
return environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE)
? new JdbcTransactionManager(dataSource) : new DataSourceTransactionManager(dataSource);
}
}
我们看在构造事务管理器交给 Spring 的时候会执行 TransactionManagerCustomizer#customize 方法,我们再观察 ExecutionListenersTransactionManagerCustomizer 的实现方法
class ExecutionListenersTransactionManagerCustomizer
implements TransactionManagerCustomizer<ConfigurableTransactionManager> {
private final List<TransactionExecutionListener> listeners;
ExecutionListenersTransactionManagerCustomizer(List<TransactionExecutionListener> listeners) {
this.listeners = listeners;
}
@Override
public void customize(ConfigurableTransactionManager transactionManager) {
//向事务管理器中添加 TransactionExecutionListener
this.listeners.forEach(transactionManager::addListener);
}
}
这下就很清晰了,只要我们自定义一个 TransactionExecutionListener 交给 Spring 管理即可被事务管理器应用
@Bean
public TransactionExecutionListener transactionExecutionListener() {
return new TransactionExecutionListener() {
@Override
public void beforeBegin(TransactionExecution transaction) {
System.out.println("beforeBegin");
}
//......
};
}
两者最重要的区别是 TransactionExecutionListener 作用的是所有事务,而 TransactionSynchronization 作用的是具体某个绑定到当前线程的事务,下面我们用一张表格对比两者区别
| 对比维度 | TransactionExecutionListener | TransactionSynchronization |
|---|---|---|
| 作用范围 | 全局级别:作用于事务管理器下的所有事务 | 线程级别:作用于当前线程绑定的事务 |
| 注册方式 | Spring 启动时通过事务管理器或 Bean 配置 | 运行时动态注册(通过TransactionSynchronizationManager.registerSynchronization() |
| 事务感知 | 可以获取TransactionExecution对象,了解事务全局信息 | 只能感知当前事务,无法获取事务详细信息 |
| 调用次数 | 每个事务生命周期都会触发 | 只有显式注册了的事务才触发 |
| 依赖关系 | 独立于特定线程,不依赖线程绑定 | 依赖当前线程的事务绑定状态 |
| 适用场景 | - 全局坚控和统计 - 全局限流 - 审计日志 - 性能分析 | - 业务回调(发消息、事件) - 清理线程本地缓存 - 特定业务的补偿操作 - 事务完成后的资源释放 |
| 生命周期方法 | - afterBegin- beforeCommit- afterCommit- beforeRollback- afterRollback- afterCompletion- beforeSuspend- afterSuspend- beforeResume- afterResume | - suspend- resume- flush- beforeCommit- beforeCompletion- afterCommit- afterCompletion |
| 执行顺序 | 相同阶段比同步器后执行 | 相同阶段比器先执行 |
| 异常处理 | 器中的异常会影响事务(可配置) | 同步中的异常通常不会影响事务 |
| 资源管理 | 由 Spring 容器管理生命周期 | 由当前事务管理,事务结束后自动清理 |
| 事务挂起 | 可以感知事务的挂起和恢复事件 | 可以实现挂起和恢复的回调 |
由于篇幅问题,本篇文章先介绍 Spring 事务管理的相关重要的类和接口,下一篇文章我们具体介绍声明式事务实现的源代码,以及对比 TransactionTemplate 实现编程式事务,使用事务管理器实现另一种编程式事务管理。