面试记录 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。另外你需要制定你的事务的作用方式,是每次启动一个新事务,还是有事务时使用事务,没事务时候就不使用,只是接受外部的,还是说你跟外部使用同一个事务,这需要你自己指定。
点评
优点:
- 抓住核心:@Transactional 本质是 AOP 切面
- 识别两大前提:数据库支持事务 + 必须是代理对象
- 自调用失效这个经典坑主动点出,说明有实战经验
- 异常控制抓到了关键点:默认只对 RuntimeException 回滚
- 提到了事务传播机制
不足:
- "正常是需要你捕获一下,确定你要哪些状态下才失效你的事务" —— 说反了。实际情况是:默认 checked exception(受检异常)不回滚,需要通过 @Transactional(rollbackFor = Exception.class) 显式指定让 checked exception 也回滚,或用 noRollbackFor 排除特定异常。不是"你去捕获",而是"注解声明哪些要回滚"
- "back exception" 应该是 rollback exception
- 传播机制三个描述有重复和错误
- 底层实现类没提:AbstractPlatformTransactionManager / DataSourceTransactionManager / TransactionInterceptor / TransactionAttributeSource
- 底层操作没提:Connection.setAutoCommit(false) / Connection.commit() / Connection.rollback()
- 少了事务的三要素概念:事务管理器 + 事务属性 + 事务注解
补充:
- 实现栈:
- @EnableTransactionManagement → 注册 TransactionInterceptor + BeanFactoryTransactionAttributeSourceAdvisor
- BeanFactoryTransactionAttributeSourceAdvisor 本质是 AbstractAdvisingBeanPostProcessor,在 postProcessAfterInitialization 时判断 bean 是否需要事务代理
- TransactionInterceptor.invoke() → 获取事务属性 → 委托 PlatformTransactionManager
- DataSourceTransactionManager.doBegin() → Connection.setAutoCommit(false)
- 方法成功:doCommit() → Connection.commit()
- 方法异常:判断异常是否符合回滚规则 → doRollback() → Connection.rollback()
- 事务传播机制 7 种:
- REQUIRED(默认):有则加入、无则新建
- REQUIRES_NEW:挂起外层,开新事务
- SUPPORTS:有则加入、无则非事务
- NOT_SUPPORTED:挂起外层,非事务执行
- MANDATORY:必须外层有事务,否则抛异常
- NESTED:有则嵌套(保存点)、无则新建
- NEVER:必须无外层事务,否则抛异常
- 异常回滚规则:
- 默认只对 RuntimeException / Error 回滚
- @Transactional(rollbackFor = Exception.class) 让 checked exception 也回滚
- @Transactional(noRollbackFor = BusinessException.class) 排除特定异常
- 注意:异常被 catch 吞掉,事务不会回滚
完整讲解
第一部分:知识重点剖析
核心考点定位:考察对 Spring 事务的实现机制、传播机制、失效场景的深度理解。
知识点拆解:
- 实现原理(三层结构):
- @Transactional 注解:声明式配置,本质是标记"这个 bean 的方法需要事务代理"
- AOP 拦截器(TransactionInterceptor):真正的事务切面,在方法调用前后开启/提交/回滚事务
- PlatformTransactionManager:抽象事务管理器接口,具体实现如 DataSourceTransactionManager(管理 JDBC Connection 的事务)
- 完整调用链:
@EnableTransactionManagement 启动时注册 BeanFactoryTransactionAttributeSourceAdvisor
- 该 Advisor 继承
AbstractAdvisingBeanPostProcessor,在 postProcessAfterInitialization 阶段判断 bean 是否需要事务代理
- 若需要(即有方法/类上标注 @Transactional),通过 AOP 创建代理对象
- 调用
@Transactional 方法时,代理对象先执行 TransactionInterceptor.invoke()
- 拦截器通过
TransactionAttributeSource 解析注解(获取传播机制、隔离级别、超时、只读、rollbackFor、noRollbackFor 等属性)
- 委托
PlatformTransactionManager.getTransaction() 开启事务(底层调 Connection.setAutoCommit(false))
- 反射调用目标方法,成功则
commit(),异常则判断是否符合回滚规则 → rollback()
- 事务三要素:
- 事务管理器:
PlatformTransactionManager 及其实现
- 事务属性:传播机制、隔离级别、超时、只读、rollbackFor / noRollbackFor
- 事务注解:@Transactional 及其配置属性
- 事务传播机制 7 种:
- REQUIRED(默认):有则加入、无则新建
- REQUIRES_NEW:挂起外层,开新事务
- SUPPORTS:有则加入、无则非事务
- NOT_SUPPORTED:挂起外层,非事务执行
- MANDATORY:必须外层有事务,否则抛异常
- NESTED:有则嵌套(保存点)、无则新建
- NEVER:必须无外层事务,否则抛异常
- 异常回滚规则:
- 默认只对 RuntimeException 和 Error 回滚
- 受检异常(checked exception)默认不回滚
- 通过
@Transactional(rollbackFor = Exception.class) 让所有异常都回滚
- 通过
@Transactional(noRollbackFor = BusinessException.class) 排除特定异常
- 重要:异常被 catch 吞掉,事务不会回滚(拦截器看不到异常)
- 失效场景:
- 类内部自调用(this.aMethod()):不走代理,事务失效
- 方法 private / final / static:代理无法覆写
- 方法非 public:JDK 代理只支持 public
- 目标类未注册为 Spring Bean:无代理
- 异常被 catch 吞掉:事务拦截器看不到
- 数据库不支持事务(如 MySQL MyISAM 引擎):底层无事务能力
- 传播机制选错(如 REQUIRES_NEW 导致外层事务看不到内层)
第二部分:考察方向与延伸递进
考察方向:
- 深度:三层实现结构、事务管理器底层操作
- 广度:传播机制的选型、分布式事务演进
- 应用:事务边界设计、异常处理策略、失效场景排查
延伸递进面试题:
- @Transactional 的三要素是什么?它们分别是什么类/接口?
- 解答要点:三要素是"事务管理器 + 事务属性 + 事务注解"。事务管理器是 PlatformTransactionManager 及其实现(如 DataSourceTransactionManager);事务属性是 TransactionDefinition 定义的配置(传播、隔离、超时、只读);事务注解是 @Transactional(本质是一个 TransactionAttribute,通过 TransactionAttributeSource 解析)
- REQUIRES_NEW 和 NESTED 有什么区别?各自适用场景?
- 解答要点:REQUIRES_NEW 挂起外层事务、开完全独立的新事务,内层回滚不影响外层,外层回滚内层已提交不回滚;NESTED 是在同一物理事务内用 savepoint 嵌套,内层回滚到 savepoint 但不影响外层其他部分,外层回滚则全部回滚。适用:REQUIRES_NEW 用于日志、审计等必须提交的操作;NESTED 用于批处理中的可回滚子单元
- 什么情况下应该把 @Transactional 放在 Service 层还是 DAO 层?为什么?
- 解答要点:应该放 Service 层。理由:事务边界应该和业务操作对齐(一个 Service 方法通常对应一个业务事务);DAO 层被多个 Service 共享,若在 DAO 上加事务会导致事务粒度过细;Spring 通过 AOP 代理实现事务,Service 层已持有代理,跨 DAO 调用能共享同一事务上下文
第三部分:面试者参考回答(300-600 字口语化)
面试官您好,Spring 的 @Transactional 本质上是基于 AOP 切面的声明式事务。它的实现我一般从三层结构来讲:
第一层是 @Transactional 注解本身:它只是一个声明式标记,告诉 Spring "这个 bean 的方法需要事务代理"。它内部包含一系列事务属性,比如传播机制、隔离级别、超时时间、只读标记、rollbackFor、noRollbackFor 等。
第二层是 AOP 拦截器(TransactionInterceptor):@EnableTransactionManagement 启动时会注册 BeanFactoryTransactionAttributeSourceAdvisor,它在 Bean 初始化后(postProcessAfterInitialization)判断这个 bean 是否需要事务代理,需要就通过 AOP 创建代理对象。调用 @Transactional 方法时,代理对象先执行 TransactionInterceptor.invoke():
- 通过 TransactionAttributeSource 解析注解拿到事务属性
- 委托 PlatformTransactionManager.getTransaction() 开启事务(底层调 Connection.setAutoCommit(false))
- 反射调用目标方法
- 方法成功则 commit(),方法抛异常则判断是否符合回滚规则 → rollback()
第三层是 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 嵌套在同一物理事务内,适合批处理中可回滚的子单元。