← 每日面试题 学习看板

Spring事务失效可能是哪些原因?

面试题/00java/04 框架/01Spring/05事务管理/Spring事务失效可能是哪些原因?.md

面试记录 2026-09-28

题目:Spring事务失效可能是哪些原因?

难度:difficulty/medium

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

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

我的回答

Spring 事务失效。首先 Spring 事务是它自身不提供事务,它是依赖于所属数据库支持事务才能使用事务。另外,Spring 的事务它是基于自身代理实现的,通过事务注解标注,然后通过代理对象来实现事务的。在事务代理的实现过程中,它需要首先关闭自动提交,然后在启动方法前进行启动事务,启动方法后进行提交事务,这么一整个流程。这里就限定了。首先第一点,你这个对象这里边就隐含了一些它失败的原因。首先你这个对象是要在 Spring Bean 的管理管理范围内呢,它需要是个代理。另外,它需要执行代理的方法才能执行事务。如果你是通过直接 new 或者是在本方法内去调用这个方,事务方法,它是无法进行事务支撑的。

点评

优点:

不足(严重):

补充(事务失效 10 条完整清单):

#失效原因排查点
1类未注册为 Spring Bean没有 @Service/@Component 或被 @Configuration 扫描
2通过 new 直接实例化必须通过容器 @Autowired 注入
3类内部自调用(this.method())走 this 不走代理,最常见
4方法 private / final / static代理无法覆写
5方法非 publicJDK 动态代理限制
6异常被 catch 吞掉事务拦截器看不到异常
7传播机制不匹配(如 REQUIRES_NEW)业务期望与实际传播行为不符
8数据库本身不支持事务MySQL 用 MyISAM
9@EnableTransactionManagement 未开启Spring 原生项目需要,Spring Boot 自动配置
10注解加在字段/构造器上只能加在类或方法上

加分项:

完整讲解

第一部分:知识重点剖析

核心考点定位:考察对 Spring 事务失效场景的系统性掌握和工程排查能力。

知识点拆解:

  1. 失效的两大根本原因:
  1. 完整失效清单(10 条):
  2. 类未注册为 Spring Bean:没有 @Service/@Component 或被 @Configuration 扫描
  3. 通过 new 直接实例化:必须通过容器 @Autowired 注入
  4. 类内部自调用(this.method()):走 this 不走代理,最常见
  5. 方法 private / final / static:代理无法覆写
  6. 方法非 public:JDK 动态代理限制
  7. 异常被 catch 吞掉:事务拦截器看不到异常
  8. 传播机制不匹配(如 REQUIRES_NEW):业务期望与实际传播行为不符
  9. 数据库本身不支持事务:MySQL 用 MyISAM
  10. @EnableTransactionManagement 未开启:Spring 原生项目需要,Spring Boot 自动配置
  11. 注解加在字段/构造器上:只能加在类或方法上
  1. 常见自调用规避方案:
  1. 异常处理的两大坑:
  1. 传播机制选错的典型场景:
  1. 加分项:

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

考察方向:

延伸递进面试题:

  1. 生产上如何快速定位 @Transactional 是否真的生效了?
  1. 如何用事务事件(TransactionalEventListener)把非事务代码也挂到事务的生命周期?
  1. @Transactional 里 catch 异常然后手动回滚怎么做?抛事务异常和 catch 后调用 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 有什么区别?

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

面试官您好,Spring 事务失效的场景我一般分成两大类来记:

第一类是前提不成立:

第二类是代理机制失效,这是 90% 的失效场景都在这类里。Spring 事务基于 AOP 代理实现,所以只要调用没走代理对象,事务就一定失效。具体的场景我数一下有 8 条:

  1. 类没注册成 Spring Bean:没有 @Service/@Component 标注,或 @ComponentScan 没扫到,Spring 不会给这类建代理
  2. 通过 new 直接实例化:绕过 Spring 容器,拿到的肯定是原始对象
  3. 类内部自调用(this.method()):这是最经典的坑。this 是原始对象,不走代理,事务直接失效。规避方案有四种:从其他 Bean 调用、AopContext.currentProxy()、@Lazy 自注入 self 字段、或拆成两个 Bean
  4. 方法 private / final / static:CGLIB 无法覆写 final 和 static,也无法拦截 private 方法
  5. 方法非 public:JDK 动态代理只支持 public 接口方法
  6. 异常被 catch 吞掉:事务拦截器在 finally 之前看不到异常,事务按成功提交,这是最隐蔽的"静默失效"
  7. 传播机制选错:比如 REQUIRES_NEW 会挂起外层事务,内层回滚不影响外层,业务以为整个都回滚了但其实内层已提交
  8. 数据库引擎不支持事务:这个虽然归在前提类,但线上真会遇到"MySQL 建表没指定引擎默认用 MyISAM"

几个补充建议:

实际工程中的最佳实践: