1. Spring声明式事务的本质与核心机制
声明式事务是Spring框架最受欢迎的特性之一,它让开发者通过简单的注解就能实现复杂的事务管理。与编程式事务相比,声明式事务最大的优势在于业务代码与事务管理的解耦——你不再需要手动编写beginTransaction()和commit()这样的模板代码。
Spring实现声明式事务的核心在于AOP(面向切面编程)。当你在方法上添加@Transactional注解时,Spring会在运行时为该类生成一个代理对象。这个代理对象会在方法调用前后插入事务管理的逻辑,包括:
- 开启事务(获取连接并设置autoCommit=false)
- 提交/回滚事务
- 处理事务传播行为
- 处理隔离级别和超时设置
关键细节:Spring默认使用JDK动态代理(基于接口)或CGLIB(基于类继承)来创建代理。如果目标类实现了接口,优先使用JDK代理;否则使用CGLIB。
2. 声明式事务的完整执行流程解析
2.1 注解解析阶段
当Spring容器启动时,InfrastructureAdvisorAutoProxyCreator这个Bean后处理器会扫描所有Bean的定义。发现带有@Transactional注解的类或方法时,会为其创建事务增强器(Advice)。
具体步骤:
- 解析
@Transactional的属性(传播行为、隔离级别等) - 创建
TransactionAttributeSource保存事务属性 - 创建
TransactionInterceptor作为方法拦截器
// 伪代码展示事务拦截器的核心逻辑 public Object invoke(MethodInvocation invocation) { // 1. 获取事务属性 TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute( invocation.getMethod(), invocation.getThis().getClass()); // 2. 获取事务管理器 PlatformTransactionManager tm = determineTransactionManager(txAttr); // 3. 根据传播行为决定是加入已有事务还是创建新事务 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); try { // 4. 执行业务方法 Object retVal = invocation.proceed(); // 5. 提交事务 commitTransactionAfterReturning(txInfo); return retVal; } catch (Throwable ex) { // 6. 处理回滚 completeTransactionAfterThrowing(txInfo, ex); throw ex; } }2.2 事务拦截器工作流程
当调用被@Transactional注解的方法时,实际调用链是这样的:
- 代理对象拦截方法调用
- 根据传播行为决定事务边界
PROPAGATION_REQUIRED:如果当前没有事务就新建,有就加入PROPAGATION_REQUIRES_NEW:总是新建事务PROPAGATION_NESTED:在现有事务中创建保存点
- 获取/创建事务(包括设置隔离级别、超时等)
- 执行业务方法
- 根据执行结果决定提交或回滚
常见陷阱:同一个类中的方法调用不会经过代理,因此自调用时
@Transactional会失效。解决方法是将事务方法移到另一个Bean中。
3. 核心源码深度剖析
3.1 事务管理器体系
Spring的事务抽象核心是PlatformTransactionManager接口,常见实现有:
DataSourceTransactionManager:用于JDBC和MyBatisHibernateTransactionManager:用于HibernateJpaTransactionManager:用于JPA
事务管理器的工作流程:
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) { // 1. 获取数据源连接 Object transaction = doGetTransaction(); // 2. 如果已存在事务,根据传播行为处理 if (isExistingTransaction(transaction)) { return handleExistingTransaction(definition, transaction, debugEnabled); } // 3. 检查超时设置是否有效 if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // 4. 没有现有事务,根据传播行为决定是否新建 if (definition.getPropagationBehavior() == PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException(...); } else if (definition.getPropagationBehavior() == PROPAGATION_REQUIRED || definition.getPropagationBehavior() == PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == PROPAGATION_NESTED) { // 开始新事务 return startTransaction(definition, transaction, debugEnabled); } // ...其他传播行为处理 }3.2 事务同步管理器
TransactionSynchronizationManager使用ThreadLocal保存事务状态,确保同一线程中的操作能获取到相同的事务资源。关键方法包括:
bindResource():绑定数据源连接到当前线程unbindResource():解绑资源getResource():获取当前线程绑定的资源
重要细节:这就是为什么在事务方法中,直接从DataSource获取的连接也能参与事务——它们实际上是从线程上下文中获取的同一个连接。
4. 事务传播行为的实现原理
Spring定义了7种传播行为,其中最复杂的是PROPAGATION_NESTED和PROPAGATION_REQUIRES_NEW的区别:
| 传播行为 | 实现机制 | 适用场景 |
|---|---|---|
| REQUIRED | 加入现有事务或新建 | 大多数业务方法 |
| REQUIRES_NEW | 总是挂起当前事务并新建 | 日志记录等独立操作 |
| NESTED | 在当前事务中创建保存点 | 可部分回滚的子操作 |
PROPAGATION_NESTED的实现关键代码:
// AbstractPlatformTransactionManager protected DefaultTransactionStatus prepareTransactionStatus( TransactionDefinition definition, Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources) { DefaultTransactionStatus status = new DefaultTransactionStatus( transaction, newTransaction, newSynchronization, definition.isReadOnly(), debug, suspendedResources); // 设置保存点 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { status.createAndHoldSavepoint(); } return status; }5. 事务失效的常见场景与解决方案
5.1 自调用问题
@Service public class OrderService { public void placeOrder(Order order) { validateOrder(order); // 自调用,事务失效! saveOrder(order); } @Transactional public void saveOrder(Order order) { orderDao.insert(order); } }解决方案:
- 将事务方法移到另一个Bean中
- 通过AopContext获取当前代理(需要开启
@EnableAspectJAutoProxy(exposeProxy = true))
5.2 异常处理不当
@Transactional public void updateOrder(Order order) { try { orderDao.update(order); } catch (Exception e) { // 捕获异常导致无法回滚 log.error("更新失败", e); } }解决方案:
- 让异常抛出
- 或手动设置回滚:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
5.3 数据库引擎不支持
使用MyISAM引擎的表不支持事务,必须改用InnoDB。
6. 高级特性与性能优化
6.1 事务超时计算
Spring的事务超时计算是从最外层方法开始计时的。例如:
@Transactional(timeout = 30) public void outer() { inner(); // 即使inner设置了更大超时也无效 } @Transactional(timeout = 60) public void inner() { // ... }6.2 只读事务优化
标记为@Transactional(readOnly = true)的方法:
- 可以应用Hibernate的只读会话优化
- 某些数据库会禁用写操作
- 连接池可能返回只读连接
6.3 事务事件监听
Spring 4.2+支持事务事件监听:
@Component public class TransactionListener { @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void handleAfterCommit(TransactionEvent event) { // 事务提交后处理 } }7. 最佳实践总结
注解放置原则:
- 类级别:所有public方法都应用相同事务属性
- 方法级别:需要特殊配置的方法
异常处理建议:
- 默认只对RuntimeException回滚
- 需要检查异常也回滚时:
@Transactional(rollbackFor = Exception.class)
性能考量:
- 避免在事务中进行远程调用
- 大事务拆分为多个小事务
- 只读查询使用
readOnly=true
调试技巧:
- 开启DEBUG日志查看事务边界
- 使用
TransactionSynchronizationManager.isActualTransactionActive()检查当前是否有事务
测试验证:
- 编写测试验证事务行为
- 使用
@Rollback控制测试事务
通过深入理解Spring声明式事务的实现原理,我们不仅能正确使用它,还能在出现问题时快速定位原因。记住,事务不是银弹——合理设计事务边界和传播行为,才能构建出既可靠又高性能的应用。