Spring 深度内核-核心容器与扩展机制-声明式事务的内部 AOP 实现:TransactionInterceptor 全解

概述

在《AOP 原理剖析:代理创建、拦截链与通知顺序》(AOP 上篇)中,我们深入拆解了 Spring AOP 的两种代理创建方式、ReflectiveMethodInvocation 的递归拦截链,以及通知顺序的控制机制。在《AOP 进阶:AspectJ 集成、LTW 织入与工程实践》(AOP 下篇)中,我们又进一步探讨了编译期与类加载期织入。本文将以上下篇的 AOP 知识基座为起点,展示 AOP 的能力如何被赋予具体功能------它将不再是"日志打印"这样的简单切面,而是需要精确控制传播、回滚与资源清理的事务管理器调用链。

声明式事务是 Spring AOP 最经典、最复杂的应用。2003 年 Rod Johnson 在《Expert One-on-One J2EE》中对 EJB CMT 的批判,奠定了声明式事务管理思想的雏形。而今天,Spring 的 @Transactional 仅凭一个注解就能完整管理事务,其背后是一套精密的事务拦截器系统在无声运转。本文将拆解 TransactionInterceptor 的源码,追踪从代理入口到事务完成的全过程,帮助专家真正理解"声明式"强大背后的执行清单。

核心要点

  • 事务代理基础设施@EnableTransactionManagement 注册了 TransactionInterceptorTransactionAttributeSourceBeanFactoryTransactionAttributeSourceAdvisor,三者构成了事务 AOP 的基础骨架。
  • TransactionInterceptor 源码拆解invoke 方法在 TransactionAspectSupport.invokeWithinTransaction 中组织事务全生命周期:获取事务定义→事务管理器→开启事务→执行目标→提交/回滚→清理资源。
  • 传播行为实现AbstractPlatformTransactionManager.getTransaction 根据 PROPAGATION_REQUIREDREQUIRES_NEWNESTED 等传入定义,执行事务复用、挂起、保存点创建等分支逻辑。
  • 回滚规则判断TransactionAttribute.rollbackOn(ex) 遍历一组 RollbackRule,以 instanceof 语义匹配异常,默认对 RuntimeExceptionError 回滚。
  • 事务同步机制TransactionSynchronizationManager 管理线程绑定的资源和回调列表,beforeCommitafterCommitafterCompletion 等回调在事务提交/回滚前后被精确触发。
  • 与 AOP 拦截链的协作TransactionInterceptorOrder 默认为 Ordered.LOWEST_PRECEDENCE,确保在用户自定义切面之后执行,形成"外层切面环绕内层事务"的标准模型。
graph TD subgraph g1 ["1. AOP承接回顾"] A1["代理创建与拦截链回顾"] end subgraph g2 ["2. 基础设施注册"] B1["EnableTransactionManagement解析"] B2["注册TransactionInterceptor与Advisor"] end subgraph g3 ["3. TransactionInterceptor源码拆解"] C1["invoke方法入口"] C2["invokeWithinTransaction全链路"] end subgraph g4 ["4. 事务管理器调用链"] D1["getTransaction传播处理"] D2["commit与rollback流程"] end subgraph g5 ["5. 传播行为分支"] E1["REQUIRED / REQUIRES_NEW / NESTED"] end subgraph g6 ["6. 回滚规则"] F1["RollbackRule匹配逻辑"] end subgraph g7 ["7. 事务同步"] G1["TransactionSynchronization回调"] end subgraph g8 ["8. 生产事故"] H1["经典事故排查"] end subgraph g9 ["9. 面试专题"] I1["15题深度拆解"] end g1 --> g2 --> g3 --> g4 --> g5 --> g6 --> g7 --> g8 --> g9

文章组织架构说明

  1. AOP承接回顾:简要回顾前文核心------代理创建、拦截器链与递归执行机制,为事务拦截器在拦截链中的位置和协作方式奠定基础。
  2. @EnableTransactionManagement 解析与基础设施注册 :剖析注解如何通过 TransactionManagementConfigurationSelector 导入配置,注册 TransactionAttributeSourceTransactionInterceptorBeanFactoryTransactionAttributeSourceAdvisor,并展示纯手动配置验证。
  3. TransactionInterceptor 源码拆解 :深入 invokeinvokeWithinTransaction 全链路,逐行分析事务属性获取、目标方法执行、异常判断与资源清理。
  4. 事务管理器调用链 :以 AbstractPlatformTransactionManager 为骨架,讲解 getTransactioncommitrollback 的模板方法模式,为后续传播行为分支打基础。
  5. 传播行为分支深入 :重点拆解 handleExistingTransactionREQUIREDREQUIRES_NEWNESTED 的源码分支,以及挂起/恢复、保存点机制。
  6. 回滚规则与判断机制 :从 @Transactional 属性到 RollbackRule 列表,再到底层 rollbackOninstanceof 匹配算法,阐明默认行为与自定义覆盖。
  7. 事务同步机制TransactionSynchronizationManager 如何管理回调列表,以及 beforeCommitafterCommitafterCompletion 的精确执行时机,并演示编程式注册。
  8. 生产事故排查 :以三个真实事故(REQUIRES_NEW 失效、checked 异常不回滚、同步回调连接泄漏)展示理解偏差导致的严重后果。
  9. 面试高频专题:15 道层层递进的面试题,涵盖源码级原理与系统设计题,独立于正文,适用于人才考察与自我检验。

一、AOP 承接与事务代理总览

1.1 回顾:代理创建与拦截链

在 AOP 上篇中,我们已理清:Spring 使用 ProxyFactory 或自动代理创建器(如 AbstractAutoProxyCreator)为目标 Bean 生成 JDK 动态代理或 CGLIB 子类代理。每当调用代理对象的方法时,会进入 ReflectiveMethodInvocation 的递归调用链,逐一执行匹配的 MethodInterceptor(通知),最终通过 invokeJoinpoint 调用目标方法。

这个拦截链的每一个组件都是一个 Advice 节点,按照 Order 值排序。用户自定义的切面(如日志、权限校验)通常会配置较高的优先级(较小 Order 值),以确保在业务逻辑之前执行横切逻辑。事务作为一种"环绕"型基础能力,则被安排在拦截链的末端------这种设计保证了用户切面可以影响事务内部执行的环境(如修改线程上下文),并在事务边界内异常时能被事务回滚逻辑捕获。

1.2 @Transactional 不是日志切面

@Transactional 注解所承载的语义,远比一句"打印日志"复杂。它要求在单次 proceed() 调用中,完整控制以下 JDBC 资源生命周期:

  • 从连接池获取数据库连接(如有必要)。
  • 将连接的自动提交关闭,开启数据库事务。
  • 执行目标业务方法(可能含有多次数据操作)。
  • 若业务成功,提交事务并释放连接。
  • 若业务异常,根据规则决定回滚事务并释放连接。
  • 在整个过程中,处理事务嵌套时的挂起与恢复。

这一切都由 TransactionInterceptor 在代理的拦截器链中完成。它本身实现 MethodInterceptor,不需要额外适配器。其默认 Order 值为 Ordered.LOWEST_PRECEDENCE(即 Integer.MAX_VALUE),在拦截器链的最末端执行,所以用户切面总是"包裹"在事务外部。

下文将逐步展示,这个"用户切面→事务切面→目标方法"的执行顺序,如何与 Spring 的 Order 机制协同工作。

sequenceDiagram participant Client as 调用方 participant Proxy as 代理对象 participant UA as 用户自定义切面(UserAspect) participant TI as TransactionInterceptor participant Target as 目标Service方法 Note over Proxy,TI: 拦截器链按Order排序,UA优先 Client->>Proxy: 调用方法 Proxy->>UA: 执行前置逻辑 UA->>TI: proceed() 传递控制 TI->>TI: getTransaction / 开启事务 TI->>Target: proceed() 执行业务 Target-->>TI: 返回结果或异常 TI->>TI: commit/rollback & cleanup TI-->>UA: 返回或异常传播 UA-->>Proxy: 执行后置逻辑 Proxy-->>Client: 最终返回

主旨概括 :该序列图描绘了 TransactionInterceptor 在拦截器链末尾执行,以"外层用户切面、内层事务切面"的顺序包裹目标方法。

逐层分解 :客户端调用代理对象;代理首先执行用户自定义切面(Order 更小);用户切面内调用 proceed() 将控制权传递给事务拦截器;事务拦截器完成资源获取、事务开启、目标方法执行、提交/回滚和清理;之后控制返回用户切面;最终返回客户端。

设计原理 :将事务作为最内层拦截器,确保用户切面中的数据库操作(如有)均被事务覆盖;同时事务的边界也包含用户切面的异常处理,切面抛出的异常如符合规则也会触发回滚。LOWEST_PRECEDENCE 的设计正是遵循"基础组件应在执行链内侧"的原则。

工程联系与结论 :在真实项目中,如果我们需要在事务开启前执行某些操作(如路由数据源),可以将相关切面的 Order 调整为比事务拦截器更小(优先级更高);反之,若要确保逻辑在事务提交后执行,可以注册 TransactionSynchronization 而非增加切面顺序。


二、@EnableTransactionManagement 解析与基础设施注册

2.1 注解入口:TransactionManagementConfigurationSelector

在 Spring 配置类上标注 @EnableTransactionManagement,会触发一系列基础设施的注册。其内部逻辑如下:

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default Ordered.LOWEST_PRECEDENCE;
}

@Import 导入了 TransactionManagementConfigurationSelector,它会根据 mode 属性决定加载哪个配置类:

java 复制代码
public class TransactionManagementConfigurationSelector 
        extends AdviceModeImportSelector<EnableTransactionManagement> {

    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] {AutoProxyRegistrar.class.getName(),
                                     ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {TransactionManagementConfigUtils.
                                     TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
            default:
                return null;
        }
    }
}

对于默认的 PROXY 模式,会导入两个类:

  • AutoProxyRegistrar:向容器注册 InfrastructureAdvisorAutoProxyCreator,使基础设施级 Advisor 自动生效。
  • ProxyTransactionManagementConfiguration:直接注册事务拦截器及相关 Bean。

2.2 ProxyTransactionManagementConfiguration 注册的核心 Bean

这是一个 @Configuration 类,定义了三个核心 Bean:

(1)TransactionAttributeSource

它负责从被代理的类和方法上读取 @Transactional 属性,并将其封装为 TransactionAttribute 对象。典型的实现是 AnnotationTransactionAttributeSource,内部组合了 SpringTransactionAnnotationParser(解析 @Transactional)以及 JtaTransactionAnnotationParser 等。

(2)TransactionInterceptor

核心拦截器,构造函数注入 TransactionAttributeSourceTransactionManager(或 TransactionManager Bean,通过 PlatformTransactionManager 类型查找)。

java 复制代码
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(
        TransactionAttributeSource transactionAttributeSource) {
    TransactionInterceptor interceptor = new TransactionInterceptor();
    interceptor.setTransactionAttributeSource(transactionAttributeSource);
    if (this.txManager != null) {
        interceptor.setTransactionManager(this.txManager);
    }
    return interceptor;
}

(3)BeanFactoryTransactionAttributeSourceAdvisor

这是一个 Advisor(通知器),负责将 TransactionInterceptor 与 Pointcut 绑定。其 Pointcut 由 TransactionAttributeSource 决定------如果方法或类上存在事务属性(即有 @Transactional),则该 Advisor 匹配成功,从而将 TransactionInterceptor 应用到目标代理上。

java 复制代码
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
        TransactionAttributeSource transactionAttributeSource) {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = 
            new BeanFactoryTransactionAttributeSourceAdvisor();
    advisor.setTransactionAttributeSource(transactionAttributeSource);
    advisor.setAdvice(transactionInterceptor(transactionAttributeSource));
    if (this.enableTx != null) {
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    }
    return advisor;
}

值得注意的是,TransactionInterceptor 自身就是 Advice(实现了 MethodInterceptor),不需要像普通 Aspect 那样再用 Advice 适配。

2.3 事务基础设施注册流程图

graph TD Start["EnableTransactionManagement"] --> Import{"Import"} Import -->|"PROXY模式"| Selector["TransactionManagementConfigurationSelector"] Selector --> Config["ProxyTransactionManagementConfiguration"] Config --> Bean1["TransactionAttributeSource Bean"] Config --> Bean2["TransactionInterceptor Bean"] Config --> Bean3["BeanFactoryTransactionAttributeSourceAdvisor Bean"] Bean1 --> Parse["解析Transactional生成TransactionAttribute"] Bean2 --> Inject["注入TransactionAttributeSource和TransactionManager"] Bean3 --> Bind["绑定TransactionInterceptor为Advice"] Bind --> Proxy["InfrastructureAdvisorAutoProxyCreator自动创建代理"] Proxy --> End["目标Bean拥有事务代理"]

主旨概括 :该流程图展示了从开启 @EnableTransactionManagement 到目标 Bean 被自动代理的完整 Bean 注册链路。

逐层分解 :注解通过 @Import 导入配置选择器;选择器根据代理模式加载 ProxyTransactionManagementConfiguration;该配置类依次注册属性源、拦截器和通知器;通知器将拦截器作为 Advice 绑定;InfrastructureAdvisorAutoProxyCreator 自动为匹配的 Bean 创建代理。

设计原理 :Spring 采用 @Import + ImportSelector 的方式,使注解能够根据模式选择不同的配置类,实现了代理模式和 AspectJ 模式的解耦。将事务基础设施 Bean 的 @Role 设为 ROLE_INFRASTRUCTURE,可与用户 Bean 区分,避免与普通业务 Bean 混淆。

工程联系与结论 :在纯 Spring 项目(非 Spring Boot)中,要启用声明式事务,必须显式添加 @EnableTransactionManagement。Spring Boot 的自动配置类 TransactionAutoConfiguration 实际上会为我们完成类似的工作,因此我们在启动类上通常无需再添加该注解。

2.4 内联示例:验证基础设施注册

下面是一个基于 Spring 5.x、纯 Java 配置的示例,用于验证基础设施 Bean 是否成功注册。

java 复制代码
@Configuration
@EnableTransactionManagement
@ComponentScan("com.example.txdemo")
public class TxConfig {

    @Bean
    public DataSource dataSource() {
        // 使用H2内存数据库
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .build();
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

验证代码:

java 复制代码
public class TxInfrastructureTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = 
                new AnnotationConfigApplicationContext(TxConfig.class);
        // 检查基础设施Bean
        TransactionInterceptor interceptor = 
                ctx.getBean(TransactionInterceptor.class);
        System.out.println("TransactionInterceptor: " + interceptor);

        TransactionAttributeSource tas = 
                ctx.getBean(TransactionAttributeSource.class);
        System.out.println("TransactionAttributeSource: " + tas);

        BeanFactoryTransactionAttributeSourceAdvisor advisor = 
                ctx.getBean(BeanFactoryTransactionAttributeSourceAdvisor.class);
        System.out.println("Advisor: " + advisor);
        System.out.println("Advisor Advice: " + advisor.getAdvice());
        
        ctx.close();
    }
}

运行后控制台将打印三个基础设施实例,证明框架已自动装配。AdvisorAdvice 就是 TransactionInterceptor 本身。


三、TransactionInterceptor 源码拆解

3.1 类层次与接口实现

TransactionInterceptor 的类继承结构如下:

java 复制代码
TransactionInterceptor 
  extends TransactionAspectSupport 
    implements MethodInterceptor

TransactionAspectSupport 包含了事务拦截的核心模板逻辑,其中最重要的方法是 invokeWithinTransaction

3.2 invoke 方法入口

java 复制代码
public class TransactionInterceptor extends TransactionAspectSupport 
        implements MethodInterceptor {

    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        // 获取目标类(可能是代理后的类)
        Class<?> targetClass = (invocation.getThis() != null ? 
                              AopUtils.getTargetClass(invocation.getThis()) : null);
        // 委托给父类的模板方法
        return invokeWithinTransaction(invocation.getMethod(), targetClass, 
                                       new InvocationCallback() {
            @Override
            public Object proceedWithInvocation() throws Throwable {
                return invocation.proceed();
            }
        });
    }
}

invoke 方法的工作很简单:提取目标类,并将 invocation.proceed() 封装为回调 InvocationCallback,然后全部委派给 TransactionAspectSupport.invokeWithinTransaction

3.3 invokeWithinTransaction 全链路拆解

这是事务 AOP 最核心的方法,位于 TransactionAspectSupport 中。我们对其进行逐段拆解:

java 复制代码
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
                                         final InvocationCallback invocation) throws Throwable {

    // 1. 获取事务属性来源(TransactionAttributeSource)
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 2. 从方法或类上获取TransactionAttribute(可能是null)
    final TransactionAttribute txAttr = (tas != null ? 
                                         tas.getTransactionAttribute(method, targetClass) : null);
    // 3. 根据属性获取确定的事务管理器
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    // 4. 切点标识(通常为方法全限定名),用于事务同步
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    // 分情况:CallbackPreferring 模式(编程式事务)或常规声明式
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 声明式事务标准路径
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal;
        try {
            // 执行目标方法(或下一个拦截器)
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // 异常处理:根据回滚规则决定提交或回滚
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            cleanupTransactionInfo(txInfo);
        }
        // 正常返回后提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    } else {
        // 编程式事务路径(通常不用于@Transactional),略...
    }
}

逐段解读

  • 获取 TransactionAttribute :这是 @Transactional 注解解析后的结果,包含了传播行为、隔离级别、超时、readOnly 以及回滚规则等全部定义。如果方法上没有事务属性(没有 @Transactional),则 txAttrnull,此时 createTransactionIfNecessary 将不会创建事务,直接执行目标方法。
  • 确定事务管理器determineTransactionManager 会优先使用 @TransactionaltransactionManager (或 value)属性指定的 Bean 名称,否则使用容器中默认的 PlatformTransactionManager
  • 创建事务(如有)createTransactionIfNecessary 内部调用 tm.getTransaction(txAttr),得到 TransactionStatus,并封装为 TransactionInfo 绑定到当前线程。如果 txAttr 为 null,则 TransactionStatus 为空,表示无事务。
  • 执行目标invocation.proceedWithInvocation() 真实调用业务方法。
  • 异常回滚 :在 catch 块中调用 completeTransactionAfterThrowing,该方法内部使用 txAttr.rollbackOn(ex) 判断异常是否应当触发回滚,然后调用 tm.rollback(txInfo.getTransactionStatus())tm.commit(txInfo.getTransactionStatus())
  • 正常提交 :若无异常,调用 commitTransactionAfterReturning,最终调用 tm.commit
  • 清理finally 中调用 cleanupTransactionInfo 解绑线程绑定的 TransactionInfo,并恢复挂起的事务资源。

3.4 createTransactionIfNecessary 细节

java 复制代码
protected TransactionInfo createTransactionIfNecessary(
        PlatformTransactionManager tm, 
        @Nullable TransactionAttribute txAttr, 
        final String joinpointIdentification) {

    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }
    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            status = tm.getTransaction(txAttr); // 关键调用
        }
    }
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

prepareTransactionInfo 创建 TransactionInfo 并绑定到 TransactionAspectSupportThreadLocal<TransactionInfo> 中,同时将 TransactionSynchronizationManager 的资源同步到此事务信息中。

3.5 序列图:TransactionInterceptor 内部调用

sequenceDiagram participant Proxy as 代理 participant TI as TransactionInterceptor participant TAS as TransactionAspectSupport participant TM as TransactionManager participant Target as 目标方法 Proxy->>TI: invoke(MethodInvocation) TI->>TAS: invokeWithinTransaction(method, targetClass, callback) TAS->>TAS: txAttr = tas.getTransactionAttribute(method, targetClass) alt txAttr != null TAS->>TAS: tm = determineTransactionManager(txAttr) TAS->>TM: getTransaction(txAttr) TM-->>TAS: TransactionStatus TAS->>TAS: prepareTransactionInfo绑定线程 end TAS->>Target: invocation.proceed() 执行业务 alt 业务异常 Target-->>TAS: Throwable ex TAS->>TAS: completeTransactionAfterThrowing(txInfo, ex) alt rollbackOn(ex) == true TAS->>TM: rollback(status) else TAS->>TM: commit(status) end else 业务成功 Target-->>TAS: 返回值 TAS->>TAS: commitTransactionAfterReturning(txInfo) TAS->>TM: commit(status) end TAS->>TAS: cleanupTransactionInfo(txInfo) TAS-->>TI: 返回值 TI-->>Proxy: 返回值

主旨概括 :序列图展示了 invokeWithinTransaction 从获取事务属性到最终清理的全流程,体现了其作为事务拦截核心模板的角色。

逐层分解 :代理调用 invoke,进入 invokeWithinTransaction;首先读取事务属性,如果为非空则获取事务管理器并开启事务;执行业务方法;根据异常类型及回滚规则决定事务最终状态;最后清理线程绑定的资源。

设计原理 :Spring 将事务的开启、提交、回滚、资源清理封装在该模板方法中,子类或拦截器只需提供 TransactionAttributeSourceTransactionManager 即可复用全部逻辑。TransactionInfo 的线程绑定保证了嵌套事务传播时,外层能正确感知内层的状态。

工程联系与结论 :当事务传播行为为 REQUIRES_NEW 时,getTransaction 内部会挂起外层事务,挂起的信息保存在 TransactionInfo 中。cleanupTransactionInfo 在 finally 中负责恢复挂起的事务,这确保了即使内层异常,外层事务也能正确恢复并继续。

3.6 内联示例:观察拦截器调用链

利用 SLF4J 日志观察调用过程(需配置 DataSourceTransactionManager 的日志为 DEBUG 级别)。我们写一个 Service 并配置日志:

java 复制代码
@Service
public class OrderService {
    @Transactional
    public void placeOrder() {
        // 执行数据操作
        System.out.println("Executing placeOrder...");
        // 可以故意抛RuntimeException观察回滚
    }
}

运行并观察日志输出:

ini 复制代码
DEBUG o.s.j.d.DataSourceTransactionManager - Creating new transaction with name [com.example.OrderService.placeOrder]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG o.s.j.d.DataSourceTransactionManager - Acquired Connection [...]
DEBUG o.s.j.d.DataSourceTransactionManager - Switching JDBC Connection [...] to manual commit
...
DEBUG o.s.j.d.DataSourceTransactionManager - Initiating transaction commit
DEBUG o.s.j.d.DataSourceTransactionManager - Committing JDBC transaction on Connection [...]

此日志对应 TransactionInterceptor 调用的 getTransactioncommit 等阶段,清晰展示了事务生命周期。


四、事务管理器调用链:getTransaction、commit、rollback

4.1 AbstractPlatformTransactionManager 骨架

PlatformTransactionManager 定义了事务管理的三个核心方法:getTransaction(TransactionDefinition)commit(TransactionStatus)rollback(TransactionStatus)AbstractPlatformTransactionManager 提供了这些方法的模板实现,并定义了 doGetTransactiondoCommitdoRollback 等抽象方法供具体数据源事务管理器实现(如 DataSourceTransactionManager)。

4.2 getTransaction:传播行为的处理中枢(结构总览)

getTransaction 是传播行为决策的入口,主要流程如下:

java 复制代码
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) 
        throws TransactionException {
    // 1. 获取当前事务对象
    Object transaction = doGetTransaction();
    // 2. 如果已存在事务,根据传播行为分支处理
    if (isExistingTransaction(transaction)) {
        return handleExistingTransaction(def, transaction, debugTx);
    }
    // 3. 无事务时的分支(根据传播行为创建、挂起或抛异常等)
    if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException(...);
    }
    else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
             def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
             def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        try {
            return startTransaction(def, transaction, debugTx, suspendedResources);
        } catch (RuntimeException | Error ex) {
            resume(null, suspendedResources);
            throw ex;
        }
    }
    else {
        // SUPPORTS, NOT_SUPPORTED, NEVER 等情况返回空事务状态
        ...
    }
}

关键点isExistingTransaction 判定当前线程是否已绑定事务资源;如果存在,则调用 handleExistingTransaction 进行传播行为处理(该部分将在第五部分详细展开)。本节仅聚焦于 commitrollback 的骨架。

4.3 commit 流程:嵌套与 rollbackOnly 处理

java 复制代码
public final void commit(TransactionStatus status) throws TransactionException {
    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    if (defStatus.isLocalRollbackOnly()) {
        processRollback(defStatus, false);
        return;
    }
    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
        processRollback(defStatus, true);
        return;
    }
    processCommit(defStatus);
}

processCommit 内的关键步骤:

  • 触发 TransactionSynchronization.beforeCommit/beforeCompletion
  • 根据事务状态决定:是释放保存点(嵌套),还是执行 doCommit(新事务)
  • 最后触发 afterCommitafterCompletion

4.4 rollback 流程

java 复制代码
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
    try {
        triggerBeforeCompletion(status);
        if (status.hasSavepoint()) {
            status.rollbackToHeldSavepoint();
        } else if (status.isNewTransaction()) {
            doRollback(status);
        } else {
            status.setRollbackOnly(); // 参与外部事务,仅打标记
        }
        triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    } finally {
        cleanupAfterCompletion(status);
    }
}

设计意图commitrollback 均为模板方法,最终执行的动作由具体的事务管理器实现(例如 DataSourceTransactionManager 调用 Connection.commit()Connection.rollback())。传播行为的影响已经由 getTransaction 阶段确定的事务对象和状态承载,commit/rollback 只需据此执行。


五、传播行为分支深入

在第四部分我们看到 getTransaction 将决策委托给 handleExistingTransaction。传播行为的差异在存在事务时最为显著。此部分独立拆解各种传播行为的源码分支及挂起/保存点机制。

5.1 handleExistingTransaction 源码拆解

java 复制代码
private TransactionStatus handleExistingTransaction(
        TransactionDefinition definition, Object transaction, boolean debugTx) {

    // 1. PROPAGATION_NEVER:已存在事务则抛异常
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
        throw new IllegalTransactionStateException(
                "Existing transaction found for transaction marked with propagation 'never'");
    }

    // 2. PROPAGATION_NOT_SUPPORTED:挂起当前事务,以无事务方式执行
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
        Object suspendedResources = suspend(transaction);
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(definition, null, false, newSynchronization,
                debugTx, suspendedResources);
    }

    // 3. PROPAGATION_REQUIRES_NEW:挂起当前事务,新建事务
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
        SuspendedResourcesHolder suspendedResources = suspend(transaction);
        try {
            return startTransaction(definition, transaction, debugTx, suspendedResources);
        } catch (RuntimeException | Error ex) {
            resume(transaction, suspendedResources);
            throw ex;
        }
    }

    // 4. PROPAGATION_NESTED:嵌套事务(需底层支持保存点)
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        if (!isNestedTransactionAllowed()) {
            throw new NestedTransactionNotSupportedException(
                    "Transaction manager does not allow nested transactions");
        }
        if (useSavepointForNestedTransaction()) {
            // 在现有事务中创建保存点
            DefaultTransactionStatus status =
                    prepareTransactionStatus(definition, transaction, false, false, debugTx, null);
            status.createAndHoldSavepoint();
            return status;
        } else {
            // 若不使用保存点,则类似 REQUIRES_NEW
            return startTransaction(definition, transaction, debugTx, null);
        }
    }

    // 5. PROPAGATION_REQUIRED, SUPPORTS, MANDATORY:复用当前事务
    if (isValidateExistingTransaction()) {
        validateExistingTransaction(definition, transaction);
    }
    return prepareTransactionStatus(definition, transaction, false, false, debugTx, null);
}

逐段解读

  • PROPAGATION_NEVER / NOT_SUPPORTED:一个抛异常,一个挂起事务。挂起通过 suspend 方法将当前连接资源和同步列表暂存到 SuspendedResourcesHolder 中,并解绑线程。
  • PROPAGATION_REQUIRES_NEW:挂起现有事务后,调用 startTransaction 创建全新事务(通常是一个新的数据库连接)。挂起操作必须在 try 中执行,若创建失败需恢复原事务。
  • PROPAGATION_NESTED:检查是否允许嵌套事务,并优先使用保存点(createAndHoldSavepoint),若数据库不支持保存点,则默认退化为 REQUIRES_NEW 或直接抛异常(取决于配置)。
  • 其余行为(REQUIREDSUPPORTSMANDATORY):不会挂起或新建,而是验证(如隔离级别兼容性)后复用。

5.2 挂起事务与恢复的底层实现

java 复制代码
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) {
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        List<TransactionSynchronization> suspendedSynchronizations = 
                TransactionSynchronizationManager.getSynchronizations();
        // 遍历并暂停每个TransactionSynchronization,然后清理线程同步列表
        for (TransactionSynchronization synchronization : suspendedSynchronizations) {
            synchronization.suspend();
        }
        TransactionSynchronizationManager.clearSynchronization();
    }
    // 解绑并保存资源
    Map<Object, Object> resources = TransactionSynchronizationManager.getResourceMap();
    // ... 遍历 resources,对每个持有者执行 suspend,然后解绑
    return new SuspendedResourcesHolder(resources, suspendedSynchronizations, ...);
}

挂起时:

  • 调用每个已注册的 TransactionSynchronization.suspend(),给它们一个清理机会。
  • 将当前线程绑定的所有资源(如 ConnectionHolder)从 TransactionSynchronizationManager 中移除,暂存在 SuspendedResourcesHolder 中。
  • 恢复时 (resume),重新调用 bindResource 绑定相应的资源,并将同步列表加回。

这种挂起/恢复机制是 REQUIRES_NEWNOT_SUPPORTED 实现独立事务/非事务执行的基础。

5.3 三种核心传播行为对比图

graph TD Start["getTransaction"] --> Exist{"已存在事务"} Exist -->|"是"| Handle["handleExistingTransaction"] Exist -->|"否"| NewBranch{"传播行为"} NewBranch -->|"REQUIRED 或 REQUIRES_NEW 或 NESTED"| Create["创建新事务"] NewBranch -->|"SUPPORTS 或 NOT_SUPPORTED 或 NEVER"| NoTx["返回空事务状态"] Handle -->|"REQUIRED"| Reuse["复用当前事务"] Handle -->|"REQUIRES_NEW"| SuspendNew["挂起当前事务 创建新事务"] Handle -->|"NESTED"| Savepoint["创建保存点"] Handle -->|"NOT_SUPPORTED"| SuspendNoTx["挂起 无事务执行"] Handle -->|"NEVER"| Error["抛异常"]

主旨概括:该图突出在已有事务时,三种"新建"行为(REQUIRED、REQUIRES_NEW、NESTED)的本质区别:REQUIRED 不新建,而是复用;REQUIRES_NEW 挂起原有事务并新建独立事务;NESTED 在当前连接上创建保存点。

逐层分解REQUIRED 直接返回已存在的事务状态;REQUIRES_NEW 执行挂起流程后启动一个全新的物理事务(往往是新的连接);NESTED 仅创建保存点,不改变物理连接,内层回滚不影响外层提交。

设计原理 :Spring 通过挂起机制保证 REQUIRES_NEW 可以在同一线程内启动完全独立的事务,但要求连接池能够提供足够的连接。NESTED 则利用了 JDBC 3.0 的保存点特性,在单个连接内实现子事务部分回滚,开销更小。

工程联系与结论REQUIRES_NEWNESTED 的选择直接影响数据库锁和连接资源。例如,使用 REQUIRES_NEW 可避免外层回滚影响内层审计日志,但需要额外的连接;使用 NESTED 则必须在同一个连接内,可能受数据库锁和隔离级别的限制。

5.4 内联示例:多种传播行为嵌套调用观察

下面示例演示 REQUIREDREQUIRES_NEWNESTED 三种传播行为的日志差异。

java 复制代码
@Service
public class OuterService {
    @Autowired
    private InnerService innerService;

    @Transactional(propagation = Propagation.REQUIRED)
    public void outerRequired() {
        System.out.println("--- outer REQUIRED start ---");
        innerService.innerRequiresNew();   // 内层REQUIRES_NEW
        innerService.innerNested();        // 内层NESTED
        System.out.println("--- outer REQUIRED end ---");
    }
}

@Service
public class InnerService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void innerRequiresNew() {
        System.out.println("   >>> inner REQUIRES_NEW executed");
    }

    @Transactional(propagation = Propagation.NESTED)
    public void innerNested() {
        System.out.println("   >>> inner NESTED executed");
    }
}

在日志中可观察到:

  • 外层事务 outerRequired 开启一个事务。
  • 调用 innerRequiresNew 时,日志显示 Suspending current transaction, creating new transaction,结束后 Resuming suspended transaction
  • 调用 innerNested 时,日志显示 Creating savepoint,提交时则释放保存点。

可通过 DEBUG 级别日志实际运行验证。


六、回滚规则与回滚判断机制

6.1 回滚规则的来源:@Transactional 属性

@TransactionalrollbackFornoRollbackForrollbackForClassNamenoRollbackForClassName 这些属性在被解析时,会生成一组 RollbackRule 对象。

SpringTransactionAnnotationParser 解析 @Transactional 并填充 RuleBasedTransactionAttribute

java 复制代码
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    // 设置传播行为、隔离级别等...
    // 解析回滚规则
    List<RollbackRule> rollbackRules = new ArrayList<>();
    for (Class<?> clazz : attributes.getClassArray("rollbackFor")) {
        rollbackRules.add(new RollbackRuleAttribute(clazz));
    }
    for (String className : attributes.getStringArray("rollbackForClassName")) {
        rollbackRules.add(new RollbackRuleAttribute(className));
    }
    for (Class<?> clazz : attributes.getClassArray("noRollbackFor")) {
        rollbackRules.add(new NoRollbackRuleAttribute(clazz));
    }
    for (String className : attributes.getStringArray("noRollbackForClassName")) {
        rollbackRules.add(new NoRollbackRuleAttribute(className));
    }
    rbta.setRollbackRules(rollbackRules);
    return rbta;
}

6.2 RollbackRule 继承体系与匹配逻辑

RollbackRule 是抽象类,有两个具体实现:

  • RollbackRuleAttribute:匹配的异常应触发回滚。
  • NoRollbackRuleAttribute:匹配的异常触发回滚。

匹配方法 getDepth(Throwable ex) 返回异常与规则中定义的异常类的"深度"(即继承层次差),深度越小越匹配。rollbackOn 方法综合判断:

java 复制代码
public boolean rollbackOn(Throwable ex) {
    RollbackRule winner = null;
    int deepest = Integer.MAX_VALUE;
    // 遍历所有规则,找出最匹配的(深度最小)
    for (RollbackRule rule : this.rollbackRules) {
        int depth = rule.getDepth(ex);
        if (depth >= 0 && depth < deepest) {
            deepest = depth;
            winner = rule;
        }
    }
    // 如果没有自定义规则,走默认规则:RuntimeException或Error回滚
    if (winner == null) {
        return super.rollbackOn(ex);
    }
    return !(winner instanceof NoRollbackRuleAttribute);
}

默认行为(DefaultTransactionAttribute.rollbackOn):

java 复制代码
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

6.3 回滚规则匹配流程图

flowchart TD Start[捕获异常ex] --> HasRules{存在自定义rollbackRules?} HasRules -->|是| Iterate[遍历rules计算匹配深度] HasRules -->|否| Default[默认: RuntimeException或Error?] Default -->|是| Rollback[回滚] Default -->|否| Commit[提交] Iterate --> Winner{找出最深匹配winner} Winner -->|NoRollbackRuleAttribute| Commit Winner -->|RollbackRuleAttribute| Rollback

主旨概括 :该流程图描述了从捕获异常到决定回滚或提交的判断路径:优先匹配自定义规则,无自定义规则时使用默认的 RuntimeException/Error 判断。

逐层分解rollbackOn 计算每个规则的匹配深度,选中最深匹配的规则;如果是 NoRollbackRule,即使异常属于默认回滚类型也不回滚;反之,如果是 RollbackRule 则触发回滚。

设计原理 :采用深度计算而非简单 isAssignableFrom 精确匹配,可正确处理父子异常关系。如 rollbackFor = BusinessException.class 时,其子类 PaymentException 也会回滚,除非有更精确的 noRollbackFor = PaymentException.class 覆盖。

工程联系与结论 :许多团队在事务切面上采用自定义元注解(如 @ServiceTx),并统一设置 rollbackFor = Exception.class,以避免 checked 异常不回滚的"陷阱"。理解该机制有助于在特定场景下精细控制(例如异步事件发送只在特定业务异常回滚后执行)。

6.4 内联示例:自定义回滚规则验证

java 复制代码
class BusinessException extends RuntimeException {}
class PaymentException extends BusinessException {}

@Service
public class PaymentService {
    @Transactional(rollbackFor = BusinessException.class, 
                   noRollbackFor = PaymentException.class)
    public void processPayment() throws PaymentException {
        // 若抛BusinessException会回滚,抛PaymentException不回滚
        throw new PaymentException("支付异常但不回滚");
    }
}

编写测试,分别抛出 BusinessExceptionPaymentException,通过 SQL 日志或数据库状态验证回滚与否。


七、事务同步机制(TransactionSynchronization)

7.1 TransactionSynchronizationManager 的职责

TransactionSynchronizationManager 是事务同步的核心协调器,它在每个线程中维护两组状态:

  • 资源映射 Map<Object, Object> resources:Key 为资源标识(通常是 DataSource),Value 为资源持有者(如 ConnectionHolder)。通过 bindResourceunbindResource 管理。
  • 同步回调列表 List<TransactionSynchronization> synchronizations:通过 registerSynchronization 注册,在事务各阶段触发。

7.2 TransactionSynchronization 回调时机

接口定义了以下回调:

  • beforeCommit(boolean readOnly):提交前调用(不适用于回滚)。
  • beforeCompletion():事务完成前调用(提交或回滚前)。
  • afterCommit():提交成功后调用。
  • afterCompletion(int status):事务完成后(提交或回滚后),携带状态 STATUS_COMMITTEDSTATUS_ROLLED_BACKSTATUS_UNKNOWN
  • suspend() / resume():事务挂起/恢复时专用。

AbstractPlatformTransactionManagerprocessCommitprocessRollback 中,可以看到明确的调用顺序:

提交场景

  1. triggerBeforeCommit (遍历 synchronizations,调用 beforeCommit)
  2. triggerBeforeCompletion
  3. 实际 doCommit
  4. triggerAfterCommit
  5. triggerAfterCompletion(STATUS_COMMITTED)

回滚场景

  1. triggerBeforeCompletion
  2. 实际 doRollback 或设置回滚标记
  3. triggerAfterCompletion(STATUS_ROLLED_BACK)

7.3 回调序列图

sequenceDiagram participant TM as TransactionManager participant Sync as TransactionSynchronization列表 participant DB as 数据库 TM->>Sync: triggerBeforeCommit(false) Sync-->>TM: TM->>Sync: triggerBeforeCompletion() Sync-->>TM: TM->>DB: doCommit / doRollback alt 提交成功 TM->>Sync: triggerAfterCommit() TM->>Sync: triggerAfterCompletion(STATUS_COMMITTED) else 回滚 TM->>Sync: triggerAfterCompletion(STATUS_ROLLED_BACK) end

主旨概括:该序列图展示了事务同步回调在提交/回滚流程中的准确位置,为依赖事务后期操作的逻辑提供了可靠挂载点。

逐层分解 :提交前依次触发 beforeCommitbeforeCompletion,完成数据库操作后触发 afterCommit(仅提交成功)和 afterCompletion;回滚时仅触发 beforeCompletionafterCompletion

设计原理beforeCommitafterCommit 的分离使得像发送消息、清除缓存等操作可以精确在事务提交前或后执行,避免数据不一致。afterCompletion 是最终的清理点,即使提交或回滚失败,这里也应该做防御式资源释放。

工程联系与结论 :在 afterCompletion 中绝对不要抛出异常,因为它会中断 cleanupAfterCompletion 的正常执行,导致数据库连接无法归还连接池,引发严重生产事故。

7.4 内联示例:注册事务同步实现缓存更新

java 复制代码
@Service
public class OrderService {
    @Transactional
    public void createOrder(Order order) {
        // 业务操作
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 发送消息或更新缓存,仅当事务提交成功执行
                    System.out.println("事务提交成功,更新缓存");
                }
                @Override
                public void afterCompletion(int status) {
                    if (status == STATUS_ROLLED_BACK) {
                        System.out.println("事务回滚,执行补偿");
                    }
                    // 永远不要抛异常
                }
            });
    }
}

八、生产事故排查专题

生产事故往往源于对机制理解的微小偏差。以下三个案例均来自真实场景的抽象。

案例1:REQUIRES_NEW 嵌套事务失效,内层回滚波及外层数据

事故现象 :订单服务 createOrder 调用积分服务 addPoints,积分服务标记为 REQUIRES_NEW。积分服务内部抛异常,期望只回滚积分更新,但生产中发现订单也被回滚。

排查思路

  1. 检查日志中 DataSourceTransactionManager 的事务创建和挂起操作。
  2. 观察 TransactionSynchronizationManager.getResource,发现内层事务复用了与外层相同的数据库连接(同一个 ConnectionHolder)。
  3. 连接池配置为 HikariCP,最大连接数10,检查当时连接占用情况,发现连接池耗尽,内层等待外层连接释放,实际退化为同一个事务。

根因分析 :Spring 的 REQUIRES_NEW 要求挂起外层事务并获取新连接。如果连接池无空闲连接,且超时设置不合理,DataSourceTransactionManager 可能无法获得新连接,从而在某种配置或 BUG 下回退为复用。在本案例中,连接池大小设置过小,高并发下导致死锁或等待超时,内层方法实际未独立开启事务。

解决方案:合理配置连接池大小,确保至少可以容纳最大嵌套层数的连接数。同时,添加事务监控,针对 REQUIRES_NEW 场景检测实际连接是否为新建。

最佳实践 :配置连接池 maximumPoolSize ≥ 业务并发度 * 最大嵌套深度。对关键事务添加自定义 TransactionSynchronization 监听,验证事务边界。

案例2:checked 异常未触发回滚,对账发现数据不一致

事故现象 :文件导入服务在解析文件时抛出 IOException,业务逻辑规定此时应回滚全部导入数据,然而数据库中出现部分成功记录。

排查思路 :查看异常堆栈,确认异常为 IOException。发现方法上的 @Transactional 未指定 rollbackFor。日志显示事务正常提交。

根因分析 :Spring 默认只对 RuntimeExceptionError 进行回滚,IOException 属于 checked 异常,所以被直接提交。源码 DefaultTransactionAttribute.rollbackOn 中明确排除了非运行时异常。

解决方案 :修改注解为 @Transactional(rollbackFor = Exception.class)

最佳实践 :制定团队编码规范,要求所有 @Transactional 显式指定 rollbackFor = Exception.class,或封装自定义元注解(如 @ServiceTransactional)统一管理。使用 Checkstyle/PMD 等工具检查。

案例3:事务同步回调异常导致连接泄漏

事故现象:应用运行一段时间后,数据库连接池爆满,所有请求无法获取连接,系统假死。

排查思路 :线程 dump 显示大量线程阻塞在 HikariPool.getConnection()。查看连接池监控,活跃连接持续升高,无归还。审查最近代码变更,发现新增了一个 TransactionSynchronization.afterCompletion 实现,其中调用了外部 HTTP 服务,该服务偶尔超时抛 RuntimeException

根因分析afterCompletion 中的 RuntimeException 导致 AbstractPlatformTransactionManager.cleanupAfterCompletion 没能执行,连接未被释放。连接池最终耗尽。

解决方案 :立即回滚代码,在 afterCompletion 中添加 try-catch,确保异常不被抛出。将 HTTP 调用移至异步线程或改为非核心逻辑。

最佳实践afterCompletion 必须防御式编程,包裹 try-catch 并记录日志。核心清理逻辑(如归还连接)不应依赖用户代码的正确性,但目前的 Spring 实现决定了我们必须负责。


九、面试高频专题

(以下内容独立于正文,专为面试场景设计)

  1. @EnableTransactionManagement 注解做了什么?它注册了哪些核心 Bean?
    标准回答 :它通过 @Import 导入 TransactionManagementConfigurationSelector,根据模式加载 ProxyTransactionManagementConfiguration,注册了 TransactionAttributeSourceTransactionInterceptorBeanFactoryTransactionAttributeSourceAdvisor 三个核心 Bean,并启用基础设施级自动代理。
    追问

    • TransactionAttributeSource 的实现类有哪些?如何自定义?
    • 如果不用 @EnableTransactionManagement,手动开启事务代理需要哪些步骤?
    • AdviceMode.ASPECTJ 模式下的事务是如何织入的,与代理模式有何区别?
      加分回答 :可结合 TransactionManagementConfigUtils 讲解 AspectJ 模式下的配置类路径。
  2. TransactionInterceptor 如何实现 MethodInterceptor?它的 invoke 方法内部流程怎样?
    标准回答 :它继承 TransactionAspectSupport 并实现 MethodInterceptorinvoke 方法调用父类的 invokeWithinTransaction,在该方法中获取事务属性、确定事务管理器、开启事务、执行业务、决定提交或回滚、清理资源。
    追问

    • InvocationCallback 的设计好处是什么?
    • 如果目标方法没有 @Transactional,拦截器还执行吗?
    • invokeWithinTransactioncompleteTransactionAfterThrowing 具体是如何判断回滚的?
      加分回答 :可提及该方法还支持 CallbackPreferringPlatformTransactionManager 的特殊路径。
  3. TransactionAttributeSource 和 TransactionAttribute 的作用是什么?
    标准回答TransactionAttributeSource 负责从方法/类上提取事务配置并生成 TransactionAttribute 对象,后者封装了传播行为、隔离级别、超时、回滚规则等。
    追问

    • 如何实现一个自定义的 TransactionAttributeSource
    • @Transactional 注解的属性解析在哪个类完成?
    • 多数据源场景下,TransactionAttribute 如何帮助选择事务管理器?
      加分回答AnnotationTransactionAttributeSource 内部实现了基于 @Transactional@javax.transaction.Transactional 等多种注解的解析。
  4. AbstractPlatformTransactionManager.getTransaction 方法处理了哪些传播行为?各自的逻辑差异是什么?
    标准回答 :处理 REQUIRED、REQUIRES_NEW、NESTED、SUPPORTS、NOT_SUPPORTED、NEVER、MANDATORY。REQUIRED 复用或新建;REQUIRES_NEW 挂起并新建;NESTED 创建保存点;SUPPORTS 可运行在事务或非事务;NOT_SUPPORTED 挂起;NEVER 抛异常;MANDATORY 必须存在事务。
    追问

    • isExistingTransaction 的判断依据是什么?
    • 挂起事务具体做了哪些操作?
    • 为什么 NESTED 只能在 JDBC 环境中使用?
      加分回答 :结合源码解释 SuspendedResourcesHolder 的结构和恢复流程。
  5. PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 在源码中是如何实现的?挂起事务具体做了什么?
    标准回答 :在 getTransaction 中,若存在事务,REQUIRED 直接返回;REQUIRES_NEW 调用 suspend 挂起当前事务(解绑资源和同步列表),然后 startTransaction 新建事务。挂起操作将原有 ConnectionHolderTransactionSynchronization 暂存,以便后续恢复。
    追问

    • 挂起后外层的 TransactionSynchronization 还会触发吗?
    • 如果连接池不够,REQUIRES_NEW 会怎样?
    • REQUIRES_NEW 内层提交后,外层还能看到内层修改吗?
      加分回答 :视隔离级别而定,通常用 REQUIRES_NEW 是为了独立提交,需要确保连接隔离。
  6. PROPAGATION_NESTED 与 REQUIRES_NEW 的区别是什么?NESTED 的底层实现依赖什么?
    标准回答 :NESTED 使用数据库保存点实现,内层回滚只回到保存点,外层提交时统一提交;REQUIRES_NEW 使用独立连接和独立事务。
    追问

    • 哪些数据库/驱动支持保存点?
    • NESTED 下,内层回滚后外层还能继续提交吗?
    • 如果数据库不支持保存点,Spring 如何处理?
      加分回答JdbcTransactionObjectSupport 负责保存点管理,DataSourceTransactionManager 覆盖了相应方法。
  7. 声明式事务的回滚规则是如何判断的?Spring 默认对哪些异常回滚?为什么?
    标准回答TransactionAttribute.rollbackOn 遍历 RollbackRule 列表,取最匹配者。默认实现只对 RuntimeExceptionError 回滚。这源于 EJB CMT 设计,Spring 为兼容而沿用。
    追问

    • 如果异常链中的有回滚异常,会回滚吗?
    • 自定义 RollbackRule 的深度计算原理是什么?
    • 如何全局更改默认回滚行为?
      加分回答 :可扩展 DefaultTransactionAttribute 覆盖 rollbackOn,并注册自定义 TransactionAttributeSource
  8. TransactionSynchronizationManager 的作用是什么?它如何绑定线程资源?
    标准回答 :管理当前线程的事务资源和同步回调。bindResource 将 DataSource→ConnectionHolder 存入 ThreadLocal 的 Map,getResource 取出。
    追问

    • TransactionSynchronizationManager 的生命周期是否依赖 Spring 容器?
    • 多个数据源时如何绑定不同资源?
    • 如何主动解除绑定?
      加分回答:它是静态方法驱动的,不依赖容器,但事务管理器会正确调用。
  9. TransactionSynchronization 的各个回调方法在事务生命周期中的执行顺序是什么?
    标准回答 :提交场景:beforeCommitbeforeCompletion → 实际 doCommitafterCommitafterCompletion;回滚场景:beforeCompletion → 实际回滚 → afterCompletion
    追问

    • beforeCommit 中能修改数据吗?
    • 如果 afterCompletion 抛异常会怎样?
    • @TransactionalEventListener 与它们的关系?
      加分回答@TransactionalEventListener 基于 TransactionSynchronization 实现,默认在 afterCommit 阶段触发。
  10. 事务拦截器的 Order 值是多少?如果用户自定义切面与事务切面冲突,如何控制顺序?
    标准回答 :默认为 Ordered.LOWEST_PRECEDENCEInteger.MAX_VALUE)。可在 @EnableTransactionManagementorder 属性调整,或修改 Advisor 的 Order。
    追问

    • 如果用户切面 Order 也是 LOWEST_PRECEDENCE,顺序如何确定?
    • 为何通常需要让用户切面在外层?
    • 若将事务切面顺序提前可能引发什么问题?
      加分回答:顺序的决定影响异常捕获和资源状态,提前事务可能导致用户切面中数据操作不在同一事务内。
  11. 在同一个类中,一个没有 @Transactional 的方法调用有 @Transactional 的方法,事务会生效吗?
    标准回答 :不会,因为调用是通过 this 而非代理对象,绕过了 AOP 拦截。这涉及 self-invocation 失效,详细场景及解决方案将在后续《@Transactional 失效场景大全》中展开。
    追问

    • 如何通过 AopContext 解决?
    • 使用 CGLIB 代理能解决吗?
    • 结构上如何避免?
      加分回答:本问题即为失效典型,下篇将详述。
  12. 多线程环境下,@Transactional 能传播事务吗?为什么?
    标准回答 :不能,因为事务资源(Connection)和同步状态都通过 ThreadLocal 绑定,跨线程后无法共享。
    追问

    • 如何在多线程中实现类似事务传播?
    • Spring 有提供任何跨线程的事务支持吗?
    • 分布式事务与此有何关联?
      加分回答DelegatingTransactionManager 等也无法跨线程,分布式事务才是解决跨服务/线程的方案。
  13. 为什么某些 checked 异常不会触发回滚?这是 bug 还是有意设计?底层代码是如何实现的?
    标准回答 :是有意设计,沿袭了 EJB 的传统。DefaultTransactionAttribute.rollbackOn 仅对 RuntimeExceptionError 返回 true。
    追问

    • 是否推荐修改默认行为为全部异常回滚?
    • 如何处理 SQLException 这类 checked 异常?
    • 能否通过其他方式(如 Bean 后处理器)全局覆盖?
      加分回答 :可以自定义 TransactionAttributeSource 返回修改了 rollbackOnTransactionAttribute
  14. TransactionAspectSupport.invokeWithinTransaction 中的 invokeCallback 做了什么?目标方法的返回值如何被处理?
    标准回答invokeCallback 是一个函数式接口 InvocationCallback,其唯一方法 proceedWithInvocation 调用了 MethodInvocation.proceed()。返回值直接传递,事务拦截器不做修改。
    追问

    • 如果目标方法返回 void,如何处理?
    • 返回值是否会影响事务提交?
    • 能否在拦截器中修改返回值?
      加分回答 :可以包装回调,对返回值进行后处理,但仍推荐使用 @Around 切面。
  15. (系统设计题)设计一个分布式事务追踪平台,需要记录每个事务的开始、提交、回滚时间,并关联事务嵌套关系。利用 TransactionSynchronization + MDC 实现事务上下文的传递,并讨论在 REQUIRES_NEW 场景下如何正确处理事务父子关系。
    标准回答 :利用 TransactionSynchronizationbeforeCommit / afterCompletion 记录事件,同时通过 TransactionSynchronizationManagerresource 持有 TransactionInfo。可以将跟踪 ID 放入 MDC,在事务挂起/恢复时也保存和恢复 MDC 值。
    追问

    • 如何生成全局唯一事务 ID?
    • REQUIRES_NEW 挂起/恢复时,如何处理跟踪信息的传递?
    • 如何避免大量事件记录对性能的影响?
      加分回答 :自定义 TransactionSynchronizationsuspendresume 中保存/恢复 MDC 上下文,使用 SuspendedResourcesHolder 传递跟踪信息;异步写入日志或发送到 Kafka,避免阻塞业务线程。

事务传播行为速查表

传播行为 描述 当前有事务 当前无事务 典型场景 源码分支位置
REQUIRED 默认,支持当前事务,无则新建 复用 新建 大多数业务方法 getTransaction 无事务分支直接 startTransaction;有事务分支 handleExistingTransaction 中无特殊处理直接返回
REQUIRES_NEW 新建事务,挂起当前事务 挂起并新建 新建 独立日志、审计 handleExistingTransaction → suspend → startTransaction
NESTED 嵌套事务,使用保存点 创建保存点 新建 子任务失败不干扰主任务 handleExistingTransaction → 检查 savepointAllowed → 创建保存点
SUPPORTS 支持当前事务,无则非事务运行 复用 非事务 可选的只读查询 getTransaction 无分支返回空状态;有分支直接复用
NOT_SUPPORTED 非事务运行,挂起当前事务 挂起 非事务 不需要事务的耗时操作 handleExistingTransaction → suspend
MANDATORY 必须在事务内,无则抛异常 复用 抛异常 强制要求在事务内运行 无事务时 getTransaction 直接抛异常
NEVER 必须在非事务下运行,有事务抛异常 抛异常 非事务 不允许事务的原生 JDBC handleExistingTransaction 直接抛异常

延伸阅读

  1. 《Expert One-on-One J2EE Development without EJB》Rod Johnson(第 8、9 章),声明式事务的思想源头
  2. 《Spring 揭秘》王福强,事务章节详细剖析
  3. Spring Framework 官方文档 "Transaction Management" (docs.spring.io/spring-fram...)
  4. 《Java Transaction Design Strategies》Mark Richards,深入比较事务模式
  5. Spring 源码分析系列博客:TransactionInterceptorAbstractPlatformTransactionManager 解析(社区经典)

结语 :本文作为数据访问与事务深度系列的核心枢纽,将 AOP 的通用代理机制与 @Transactional 的具体实现精密咬合。从 TransactionInterceptor 的一行 invoke,到 AbstractPlatformTransactionManager 的挂起、保存点创建与同步回调,我们看到:所谓"声明式"背后,是一条严密、可扩展且经过十数年生产考验的代码链。理解其内部运作,不仅是为了通过面试,更是为了在生产系统中避免那些看似神秘的事务失效与资源泄漏。

相关推荐
counting money1 小时前
Spring框架基础(配置篇)
java·后端·spring
生活真难1 小时前
SpringCloud - 任务调度 - xxl-job
后端·spring·spring cloud
敖正炀2 小时前
Spring 深度内核-核心容器与扩展机制-AOP 进阶:AspectJ 集成、LTW 织入与工程实践
spring
直奔標竿3 小时前
Java开发者AI转型第二十二课!Spring AI 个人知识库实战(一)——架构搭建与核心契约落地
java·人工智能·后端·spring·架构
曹牧4 小时前
Spring WebService 的两种主流实现方式‌
java·后端·spring
直奔標竿5 小时前
Java开发者AI转型第二十三课!Spring AI个人知识库实战(二):异步ETL流水线搭建与避坑指南
java·人工智能·spring boot·后端·spring
JAVA面经实录9175 小时前
Spring Boot + Spring AI 一体化实战全文档
java·人工智能·spring boot·spring
希望永不加班5 小时前
SpringBoot 接口签名验证(AppKey/Secret)
java·spring boot·后端·spring
曹牧7 小时前
Spring:@RequestMapping 注解匹配顺序
java·后端·spring