← 每日面试题 学习看板

Spring中@Transactional事务的实现原理

面试题/00java/04 框架/01Spring/05事务管理/Spring中@Transactional事务的实现原理.md

面试记录 2026-09-28

题目:Spring中@Transactional事务的实现原理

难度:difficulty/medium

标签:spring/transaction, general/knowledge, difficulty/medium

评分:⭐⭐⭐⭐☆(4/5)

我的回答

Spring 中事务注解的实现原理,本质上就是在事务方法上增加注解,增加注解的目的就是把事务的切面增加进去,在调用方法前先开启事务,调用方法后进行 commit,最终实现整个事务的实现。

从这可以看出,它的事务的实现原理是基于两个层面。首先数据库要支持事务,它支持开启事务跟提交事务。另外,当前的对象需要是 AOP 的代理对象,才能使用到 Spring 中的这个事务注解,否则事务注解是没有意义的。

比如你在一个方法中,一个类的方法中调用类中的另一个方法。其中 A 方法是调用方,B 方法是被调用方。如果 B 方法有事务注解,A 方法没有,你方在类中直接调用,你的事务注解是无效的,并不能产生作用,因为并没有经历过代理,没有相关的提交跟启动注解,启动事务。

另外事务注解有它的一些自定义的配置,包括在什么状态下失效,哪些异常状态下捕获才能失效。它默认的是 runtime exception 吧。正常是需要你捕获一下,确定你要哪些状态下才失效你的事务,进行回滚,back exception。另外你需要制定你的事务的作用方式,是每次启动一个新事务,还是有事务时使用事务,没事务时候就不使用,只是接受外部的,还是说你跟外部使用同一个事务,这需要你自己指定。

点评

优点:

不足:

补充:

  1. 实现栈:
  1. 事务传播机制 7 种:
  1. 异常回滚规则:

完整讲解

第一部分:知识重点剖析

核心考点定位:考察对 Spring 事务的实现机制、传播机制、失效场景的深度理解。

知识点拆解:

  1. 实现原理(三层结构):
  1. 完整调用链:
  1. 事务三要素:
  1. 事务传播机制 7 种:
  1. 异常回滚规则:
  1. 失效场景:

第二部分:考察方向与延伸递进

考察方向:

延伸递进面试题:

  1. @Transactional 的三要素是什么?它们分别是什么类/接口?
  1. REQUIRES_NEW 和 NESTED 有什么区别?各自适用场景?
  1. 什么情况下应该把 @Transactional 放在 Service 层还是 DAO 层?为什么?

第三部分:面试者参考回答(300-600 字口语化)

面试官您好,Spring 的 @Transactional 本质上是基于 AOP 切面的声明式事务。它的实现我一般从三层结构来讲:

第一层是 @Transactional 注解本身:它只是一个声明式标记,告诉 Spring "这个 bean 的方法需要事务代理"。它内部包含一系列事务属性,比如传播机制、隔离级别、超时时间、只读标记、rollbackFor、noRollbackFor 等。

第二层是 AOP 拦截器(TransactionInterceptor):@EnableTransactionManagement 启动时会注册 BeanFactoryTransactionAttributeSourceAdvisor,它在 Bean 初始化后(postProcessAfterInitialization)判断这个 bean 是否需要事务代理,需要就通过 AOP 创建代理对象。调用 @Transactional 方法时,代理对象先执行 TransactionInterceptor.invoke():

第三层是 PlatformTransactionManager:抽象的事务管理器接口,JDBC 场景下由 DataSourceTransactionManager 实现,它持有 DataSource,通过 Connection 对象操作事务。

两大前提:一是数据库本身支持事务(比如 MySQL InnoDB 支持,MyISAM 就不支持),二是当前对象必须是 AOP 代理对象,否则 @Transactional 完全无效。

最经典的失效场景是自调用:同一个类里的 A 方法调 B 方法,B 上标了 @Transactional,通过 this.bMethod() 调用时不走代理,事务注解无效。规避方案是从其他 Bean 调用、用 AopContext.currentProxy()、@Lazy 自注入 self 字段、或拆成两个 Bean。

异常回滚规则是关键:默认只对 RuntimeException 和 Error 回滚,受检异常(checked exception)默认不回滚。如果业务需要让 checked exception 也回滚,要显式写 @Transactional(rollbackFor = Exception.class);如果不想让某些特定异常回滚,用 noRollbackFor 排除。另外还有一个坑:如果异常被 catch 吞掉了,事务拦截器看不到异常,事务也不会回滚。

传播机制 7 种最常见的是 REQUIRED(默认)—— 有事务就加入、没有就新建;REQUIRES_NEW 会挂起外层事务、开完全独立的新事务,适合审计、日志这种必须提交的操作;NESTED 用 savepoint 嵌套在同一物理事务内,适合批处理中可回滚的子单元。