导读:本期聚焦于小团团创作的《Spring 中 @Transactional 的 AOP 通知实现原理是什么?源码级详解事务切面执行流程》,敬请观看详情。为什么在方法上加一个 @Transactional 注解,Spring 就能自动管理事务的开启、提交和回滚?答案藏在 AOP 动态代理之中。本文从源码层面拆解事务切面 BeanFactoryTransactionAttributeSourceAdvisor 的注册过程,追踪 TransactionInterceptor 环绕通知如何通过 PlatformTransactionManager 获取事务、绑定 Connection 到 ThreadLocal,以及异常发生时依据 rollbackFor 规则决定回滚还是提交。同时分析自调用失效、传播行为嵌套、代理对象创建时机等常见问题的根源,帮助你真正理解声明式事务的底层执行逻辑,避免踩坑。

声明式事务是 Spring 最受欢迎的特性之一,开发者只需在方法或类上标注一个 @Transactional,方法的执行就会被事务包裹起来,异常时自动回滚,正常时自动提交。这层看似神奇的能力,本质上是一套基于 AOP 动态代理的拦截机制。理解它的实现原理,不仅能回答面试中的高频问题,更能解释日常开发中事务失效、传播行为异常等现象的根本原因。本文将从注解解析、代理创建、拦截器执行、事务管理器协作四个层面,完整拆解这套机制。

Spring 中 @Transactional 的 AOP 通知实现原理是什么?源码级详解事务切面执行流程

一、事务切面是如何被注册到容器中的

Spring Boot 中事务功能通过自动配置类 TransactionAutoConfiguration 开启。当容器检测到存在 PlatformTransactionManager 的 Bean 时,会导入 EnableTransactionManagementConfiguration,它等价于在配置类上标注了 @EnableTransactionManagement。这个注解通过 @Import 引入了 TransactionManagementConfigurationSelector,最终向容器注册两个关键 Bean。

第一个是 TransactionInterceptor,也就是事务拦截器,它继承自 MethodInterceptor,是真正干活的通知逻辑。第二个是 BeanFactoryTransactionAttributeSourceAdvisor,这是事务切面,它内部持有拦截器和一个 TransactionAttributeSource,用于判断哪些方法需要被事务增强。两者的装配代码大致如下:

@Configuration
public class ProxyTransactionManagementConfiguration {

    @Bean
    public TransactionAttributeSource transactionAttributeSource() {
        // 负责解析 @Transactional 注解,封装事务属性
        return new AnnotationTransactionAttributeSource();
    }

    @Bean
    public TransactionInterceptor transactionInterceptor(
            TransactionAttributeSource tas) {
        return new TransactionInterceptor(null, tas);
    }

    @Bean(name = Advisor.TRANSACTION_ADVISOR_BEAN_NAME)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
            TransactionAttributeSource tas, TransactionInterceptor ti) {
        // 切面 = 切点(由 TransactionAttributeSource 提供) + 通知(拦截器)
        BeanFactoryTransactionAttributeSourceAdvisor advisor =
                new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(tas);
        advisor.setAdvice(ti);
        return advisor;
    }
}

注意这里没有一个显式的 Pointcut 表达式。事务的切点判断逻辑隐藏在 TransactionAttributeSource 中:它通过 TransactionAttributeSourcePointcutmatches 方法,检查目标方法或目标类上能否解析出 @Transactional 注解。解析过程由 AnnotationTransactionAttributeSource 委托给 SpringTransactionAnnotationParser 完成,注解中的传播行为、隔离级别、超时时间、回滚规则等属性会被封装成 RuleBasedTransactionAttribute 对象并缓存起来,避免重复反射解析。

切面 Bean 注册之后,AbstractAdvisorAutoProxyCreator 会在每个 Bean 初始化后遍历所有 Advisor,只要切点匹配成功,就会为这个 Bean 创建代理对象。这就是为什么加了注解的 Service 类,注入进来的是代理而不是原始对象。

二、TransactionInterceptor 环绕通知的完整执行流程

当外部调用代理对象的方法时,调用链最终进入 TransactionInterceptorinvoke 方法。它是标准的环绕通知,核心逻辑浓缩后如下:

public Object invoke(MethodInvocation invocation) throws Throwable {
    Class<?> targetClass = AopUtils.getTargetClass(invocation.getThis());
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 1. 解析当前方法的事务属性(含注解元数据)
    TransactionAttribute txAttr = tas.getTransactionAttribute(
            invocation.getMethod(), targetClass);

    // 2. 获取事务管理器,例如 DataSourceTransactionManager
    PlatformTransactionManager tm = determineTransactionManager(txAttr);

    // 3. 创建事务,得到 TransactionInfo(状态 + 旧事务信息)
    TransactionInfo txInfo = createTransactionIfNecessary(
            tm, txAttr, invocation.getMethod().getSimpleName());

    Object retVal;
    try {
        // 4. 执行原始业务方法,即真正的事务边界内的代码
        retVal = invocation.proceed();
    } catch (Throwable ex) {
        // 5. 异常时判断是否回滚,并抛出原异常
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    } finally {
        cleanupAfterCompletion(txInfo);
    }
    // 6. 正常返回时提交事务
    commitTransactionAfterReturning(txInfo);
    return retVal;
}

第 3 步是最关键的部分。createTransactionIfNecessary 会调用 PlatformTransactionManagergetTransaction 方法,以最常用的 DataSourceTransactionManager 为例,它会执行三件事:开启数据库连接并调用 setAutoCommit(false),将连接通过 TransactionSynchronizationManager 绑定到当前线程的 ThreadLocal(key 是 DataSource),再把 ConnectionHolder 放入新建的 TransactionStatus。此后,MyBatis 或 JdbcTemplate 获取连接时,都是从这个 ThreadLocal 里拿同一个连接,从而保证多个 SQL 落在同一事务内。

第 5 步的回滚判断由 RuleBasedTransactionAttribute.rollbackOn 完成。默认规则是:异常类型是 ErrorRuntimeException 时回滚,受检异常(如 Exception 的直接子类)则提交。这就是为什么手动抛出受检异常事务不会回滚,除非在注解上显式声明 rollbackFor = Exception.class。执行顺序上,Spring 会先匹配注解中配置的 rollbackFornoRollbackFor 规则列表,命中即返回结论,都没命中才走默认规则。

三、常见事务失效场景的原理分析

理解了拦截机制,很多失效场景就能从根源上解释。最典型的是自调用失效:同一个类中方法 A 调用带注解的方法 B,事务不生效。因为 A 中通过 this.b() 调用的是原始对象的方法,绕过了代理,拦截器根本没有机会介入。解决办法包括注入自身代理对象(通过 AopContext.currentProxy() 开启 exposeProxy)或把方法拆到另一个 Bean 中。

其次是传播行为的实现。PROPAGATION_REQUIRES_NEW 会把当前线程绑定的旧连接挂起(保存到挂起资源栈),开启新连接执行内层事务,结束后再恢复旧连接;PROPAGATION_NESTED 则是通过 Savepoint 在同一连接上打保存点,回滚时只回滚到保存点。这两种行为依赖底层驱动的支持,例如 MySQL 需要支持保存点的数据源配置,否则会抛出 NestedTransactionNotSupportedException

另外要注意异常被 try-catch 吞掉的情况。拦截器只感知穿过 invocation.proceed() 的异常,如果业务代码在方法内部把异常捕获且没有重新抛出,拦截器会认为方法正常结束并执行提交,回滚自然不会发生。同理,方法必须是 public 的,因为默认的代理策略(JDK 动态代理或 CGLIB)对非 public 方法无法保证拦截生效,Spring 会直接跳过非 public 方法上的注解解析。

四、总结:一条注解背后的完整链路

把整条链路串起来看:容器启动时,@EnableTransactionManagement 注册了事务切面和拦截器;Bean 初始化阶段,AnnotationTransactionAttributeSource 解析注解元数据,匹配成功的 Bean 被创建为代理;运行期调用到达代理,进入 TransactionInterceptor 环绕通知,由事务管理器开启连接、绑定 ThreadLocal、执行业务方法,最后依据异常类型和回滚规则决定提交或回滚。

掌握这条链路的价值在于遇到问题时有明确的排查方向:先确认调用的是代理对象而非 this,再确认异常类型是否命中回滚规则,最后检查事务管理器与数据源的绑定关系。声明式事务并不神秘,它只是把样板代码从业务方法中搬到了拦截器里,理解了 AOP 的通知模型,事务、缓存、异步等所有基于代理的 Spring 特性也就一通百通了。

@TransactionalSpring事务AOP通知修改时间:2026-09-04 21:14:40

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260904/50483.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。