共计 4222 个字符,预计需要花费 11 分钟才能阅读完成。
一、为什么要深入理解 @Transactional
?
@Transactional
是 Spring 框架中使用最广泛的注解之一,尤其在微服务、分布式系统与业务系统中,它承载了关键数据一致性的责任。
但在实际使用中,我们常常遇到以下困惑:
- 为什么
@Transactional
加在 private 方法上事务不生效? - 调用同类方法为什么事务失效?
- 多个事务嵌套时,是谁控制了回滚逻辑?
- REQUIRES_NEW、NESTED 等传播行为是如何实现的?
- rollbackFor、noRollbackFor 是怎么起作用的?
要彻底搞懂这些问题,必须深入理解其底层实现原理与传播机制的源码细节。
二、@Transactional
的核心原理概览
Spring 中的事务机制是通过 AOP(面向切面编程) 实现的。事务的执行流程简要如下:
@Transactional 注解
↓
TransactionAttributeSource(注解解析)
↓
TransactionInterceptor(拦截器处理)
↓
PlatformTransactionManager(事务管理器)
↓
数据库连接的事务控制(提交 / 回滚)
核心组件
组件 | 作用 |
@Transactional | 事务元数据标记 |
TransactionAttributeSource | 读取事务配置 |
TransactionInterceptor | AOP 拦截器,控制事务边界 |
PlatformTransactionManager | 真正执行事务的控制器 |
TransactionSynchronizationManager | 存储事务上下文(线程变量) |
三、@Transactional 是如何生效的?
1.注解驱动的入口:@EnableTransactionManagement
我们在 Spring Boot 中通常不显式声明 @EnableTransactionManagement
,但它已经由自动配置类引入。它的作用是启用基于代理的事务功能。
关键类
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement { ... }
它导入的核心类是:
ProxyTransactionManagementConfiguration
:配置 AOP 拦截器TransactionInterceptor
:拦截方法,处理事务TransactionAttributeSource
:从方法上读取事务注解信息
2 AOP 代理创建:事务切面注册过程
@Bean
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(...) {
return new BeanFactoryTransactionAttributeSourceAdvisor();
}
BeanFactoryTransactionAttributeSourceAdvisor
是一个 AOP Advisor,具备以下能力:
- 使用
TransactionAttributeSourcePointcut
匹配@Transactional
方法 - 配置
TransactionInterceptor
作为 Advice - 注册到 Spring 的 AOP 容器中
其执行流程由 Spring AOP 的 AnnotationAwareAspectJAutoProxyCreator
自动创建代理。
四、TransactionInterceptor 源码解析(重点)
1.拦截方法入口
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Class<?> targetClass = invocation.getThis().getClass();
return invokeWithinTransaction(method, targetClass, invocation::proceed);
}
这是 AOP 调用 @Transactional
方法时的核心处理逻辑
2.invokeWithinTransaction 核心处理流程
protected Object invokeWithinTransaction(...) {
// 1. 获取事务属性(注解)
TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(...);
// 2. 获取事务管理器
PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 3. 构造事务信息
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, ...);
Object retVal;
try {
// 4. 执行目标方法
retVal = invocation.proceed();
// 5. 提交事务
commitTransactionAfterReturning(txInfo);
} catch (Throwable ex) {
// 6. 回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
} finally {
// 7. 清理事务上下文
cleanupTransactionInfo(txInfo);
}
return retVal;
}
3.事务传播机制的控制:createTransactionIfNecessary
事务传播行为决定是否要新开一个事务,或者加入已有事务,关键代码如下:
public final TransactionStatus getTransaction(TransactionDefinition definition) {
Object transaction = doGetTransaction(); // 从线程变量中获取连接或事务上下文
if (isExistingTransaction(transaction)) {
// 当前存在事务,根据传播行为判断
switch (definition.getPropagationBehavior()) {
case PROPAGATION_REQUIRES_NEW:
suspend(transaction); // 挂起旧事务
return startTransaction(definition); // 新建事务
case PROPAGATION_NESTED:
return createSavepoint(transaction);
case PROPAGATION_REQUIRED:
return participateInTransaction(transaction); // 参与已有事务
...
}
} else {
// 不存在事务,根据传播行为判断是否新建
if (definition.getPropagationBehavior() == PROPAGATION_REQUIRED
|| definition.getPropagationBehavior() == PROPAGATION_REQUIRES_NEW) {
return startTransaction(definition);
}
// SUPPORTS / NOT_SUPPORTED 等则不创建事务
}
}
五、事务传播机制详解
Spring 提供了 7 种事务传播行为:
传播行为 | 说明 | 场景示例 |
---|---|---|
REQUIRED |
默认行为,有就加入,没有就新建 | 大多数业务场景 |
REQUIRES_NEW |
挂起当前事务,新建事务 | 日志记录等不可与主事务共命运 |
NESTED |
嵌套事务,使用 savepoint | 局部回滚 |
SUPPORTS |
有事务就参与,无事务就非事务执行 | 非关键业务 |
NOT_SUPPORTED |
无事务执行,挂起当前事务 | 查询场景 |
NEVER |
必须无事务,否则抛异常 | 限制性强 |
MANDATORY |
必须存在事务,否则抛异常 | 强依赖主事务的子任务 |
六、异常回滚策略(rollbackFor 的实现机制)
默认情况下,Spring 只会在出现 未检查异常(RuntimeException)或 Error 时才会回滚事务。
可以通过如下方式控制回滚策略:
@Transactional(rollbackFor = Exception.class)
源码逻辑:
if (txAttr.rollbackOn(ex)) {
txManager.rollback(status);
} else {
txManager.commit(status);
}
rollbackOn方法:
public boolean rollbackOn(Throwable ex) {
if (rollbackForClassNames.contains(ex.getClass().getName())) return true;
if (ex instanceof RuntimeException || ex instanceof Error) return true;
return false;
}
七、失效场景剖析与解决
场景 | 说明 | 解决方案 |
---|---|---|
方法是 private | Spring AOP 基于代理,无法拦截 private 方法 | 使用 public |
自调用 | 同类方法内部调用不会走代理 | 拆分到独立 Bean 中 |
异步调用 | @Async 与事务线程分离 |
改为同步或传递上下文 |
配置错误 | 忘记开启事务或未注入 PlatformTransactionManager | 添加注解或配置 |
非运行时异常 | 默认不回滚 | 指定 rollbackFor |
八、总结与最佳实践
推荐用法:
- 默认使用
REQUIRED
,特殊场景使用REQUIRES_NEW
- 不要在构造函数或 @PostConstruct 中开启事务
- 分离事务控制逻辑,避免大事务控制所有流程
- 明确异常控制规则,显式声明 rollbackFor
- 避免事务方法之间的直接互相调用(自调用失效)
九、总结
Spring 的事务机制虽然强大,但也有很多坑。理解其原理和源码实现,不仅有助于写出更加健壮、正确的业务代码,也有助于我们在系统设计中合理地拆分职责和控制边界。
十、关注后续专栏
本文是《Spring 源码解析系列》专栏的第 2 篇,后续将发布:
- 《Spring AOP 原理与 CGLIB / JDK 动态代理源码分析》
- 《Spring 中的事件监听机制(ApplicationEvent)源码与实战》