划分处理单元 IOC

由于 Spring 解决的问题是对单个数据库进行局部事务处理的,具体的实现首相用 Spring 中的 IOC 划分了事务处理单元。并且将对事务的各种配置放到了 IOC 容器中(设置事务管理器,设置事务的传播特性及隔离机制)。

AOP 拦截需要进行事务处理的类

Spring 事务处理模块是通过 AOP 功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用 TransactionProxyFactoryBean 接口来使用 AOP 功能,生成 proxy 代理对象,通过 TransactionInterceptor 完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。读取 IOC 容器事务配置属性,转化为 Spring 事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为 TransactionAttribute 表示的数据对象。

对事物处理实现(事务的生成、提交、回滚、挂起)

Spring 委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource 数据源支持、Hibernate 数据源事务处理支持、JDO 数据源事务处理支持,JPA、JTA 数据源事务处理支持。这些支持都是通过设计 PlatformTransactionManager、AbstractPlatforTransaction 一系列事务处理的支持。 为常用数据源支持提供了一系列的 TransactionManager。

结合

PlatformTransactionManager 实现了 TransactionInterception 接口,让其与 TransactionProxyFactoryBean 结合起来,形成一个 Spring 声明式事务处理的设计体系。

IOC 划分 + AOP 拦截 + 事务管理器适配三层骨架正确,但现代实现细节已演进(注解驱动替代 TransactionProxyFactoryBean),且还缺一块最核心的机制:事务上下文如何与线程/连接绑定(TransactionSynchronizationManager)。本文补全链路,把”注解生效到 SQL 提交”的每一步落到对象级。

现代链路:从 @Transactional 到代理

  1. 解析:AnnotationTransactionAttributeSource 读取方法/类上的 @Transactional,封装为 TransactionAttribute(传播/隔离/超时/只读/回滚规则)
  2. 匹配:BeanFactoryTransactionAttributeSourceAdvisor(一个 Advisor)以”是否有 TransactionAttribute”为 pointcut 匹配 bean
  3. 代理:匹配成功的 bean 在初始化后被织入代理,拦截器为 TransactionInterceptor
  4. 执行:代理方法调用进入 TransactionInterceptor.invoke → 委托 TransactionAspectSupport.invokeWithinTransaction

invokeWithinTransaction 的六步

// 伪码主线
TransactionAttribute attr = 解析注解;
PlatformTransactionManager tm = 确定事务管理器;
TransactionInfo txInfo = createTransactionIfNecessary(attr, tm);
try {
ret = invocation.proceedWithInvocation(); // 执行业务方法
} catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex); // 按 rollbackFor 规则回滚
throw ex;
} finally {
cleanupTransactionInfo(txInfo); // 恢复外层事务信息(传播支持)
}
commitTransactionAfterReturning(txInfo); // 提交

createTransactionIfNecessary 内部按传播行为决策:REQUIRED 查当前线程有无事务(有则加入)、REQUIRES_NEW 挂起当前事务新开、NESTED 建保存点等——传播语义全部在这一分支里实现。

线程绑定:TransactionSynchronizationManager

事务的”当前连接”存在哪?答案是一个基于 ThreadLocal 的资源持有器:

  • DataSourceTransactionManager.doBegin:从连接池取 Connection,setAutoCommit(false),绑定到 TransactionSynchronizationManager 的 ThreadLocalMap(key=DataSource)
  • 业务中 JdbcTemplate/MyBatis 获取连接时,先查 ThreadLocal:同线程同 DataSource 拿到同一个 Connection——这就是”一个事务一个连接”的保证
  • 提交/回滚后解绑、恢复 autoCommit、归还连接

这也解释了多线程失效的根因:子线程查不到父线程 ThreadLocal 里的连接,自然不在同一事务。

挂起与恢复(REQUIRES_NEW / NESTED)

  • REQUIRES_NEW:suspend 把当前 TransactionInfo 与连接绑定从 ThreadLocal 摘除保存 → 新开连接与事务 → 内层结束后 resume 恢复外层绑定。内外层是两个独立 Connection、两个独立事务
  • NESTED:同一连接上 SAVEPOINT,内层回滚只滚到保存点,外层提交时一并提交

回滚规则与标记

completeTransactionAfterThrowing 判断:异常类型匹配 rollbackFor/noRollbackFor 规则 → tm.rollback;否则提交。另支持 setRollbackOnly():在 TransactionStatus 上打标记,提交时检查标记强制回滚(编程式控制点)。

事务管理器家族(适配层)

  • DataSourceTransactionManager:单 JDBC 数据源(最常用)
  • JpaTransactionManager / HibernateTransactionManager:ORM 会话与事务联动
  • JtaTransactionManager:跨多资源(多数据源/MQ)的 JTA/XA 分布式事务
  • 抽象基类 AbstractPlatformTransactionManager 固化了 begin/commit/rollback 模板流程(模板方法模式),子类只实现 doBegin/doCommit/doRollback

一张图总结

@Transactional
↓ 解析
TransactionAttribute ← AnnotationTransactionAttributeSource
↓ 匹配(AOP Advisor)
代理对象 → TransactionInterceptor 拦截
↓ 传播决策
PlatformTransactionManager.doBegin → Connection 绑 ThreadLocal
↓ 业务执行(同线程复用连接)
commit / rollback(按规则与标记)→ 解绑归还连接

底层原理的主线:AOP 提供拦截点、TransactionAttribute 携带规则、传播行为决定事务拓扑、ThreadLocal 绑定连接保证同事务同连接、管理器适配不同数据源。五者各管一段,声明式事务才是”一个注解”背后完整的协作体系。