PDF大白话说Java面试题 — 06_Spring篇第20题Spring 事务什么时候会失效回答核心考点Spring 事务失效是面试中最高频的考点之一大厂面试官不会满足于背诵非 public、同类调用等表面原因而是期望你深入理解AOP 代理机制的本质JDK 动态代理 vs CGLIB 的字节码重写边界、事务拦截的完整链路从TransactionInterceptor到ThreadLocal绑定、异常回滚的精确语义默认仅回滚 RuntimeException/Error以及传播行为对事务边界的影响。面试官真正想判断的是你是否能从源码级理解为什么失效以及能否在生产环境中快速定位和修复事务问题。1. 代理机制导致的失效4 种场景Spring 声明式事务基于 AOP 代理实现任何导致代理无法生效的情况都会使事务失效。1.1 方法非 public原因TransactionInterceptor通过 AOP 代理拦截方法调用而 JDK 动态代理只能代理接口的 public 方法CGLIB 代理虽然可以代理类但 Spring 的事务属性解析器AbstractFallbackTransactionAttributeSource明确过滤了非 public 方法// AbstractFallbackTransactionAttributeSource 源码protectedTransactionAttributecomputeTransactionAttribute(Methodmethod,Class?targetClass){// 只允许 public 方法if(allowPublicMethodsOnly()!Modifier.isPublic(method.getModifiers())){returnnull;// 非 public 方法直接返回 null不生成事务属性}// ...}示例ServicepublicclassUserService{TransactionalprivatevoidaddUser(){// ❌ 事务失效userDao.insert(user);}}解决方案将方法改为public。[citation:0]1.2 方法被 final/static 修饰原因final 方法CGLIB 代理通过生成子类并重写父类方法实现代理final方法无法被重写代理对象无法插入事务逻辑。static 方法静态方法属于类而非实例CGLIB 代理的是实例对象无法拦截静态方法调用。示例ServicepublicclassUserService{TransactionalpublicfinalvoidaddUser(){// ❌ final 方法事务失效userDao.insert(user);}TransactionalpublicstaticvoiddeleteUser(){// ❌ static 方法事务失效userDao.delete(id);}}解决方案移除final/static修饰符。[citation:1]1.3 Bean 未被 Spring 容器管理原因Spring 事务依赖 IoC 容器创建代理对象。通过new关键字创建的对象是普通 Java 对象没有经过BeanPostProcessor处理不会生成事务代理。示例// ❌ 错误new 出来的对象没有代理UserServiceuserServicenewUserService();userService.addUser();// 事务失效// ✅ 正确从 Spring 容器获取代理对象AutowiredprivateUserServiceuserService;userService.addUser();// 事务生效1.4 同类方法自调用this 调用原因this.methodB()调用的是目标对象本身的方法而非代理对象的方法。事务逻辑开启、提交、回滚编织在代理对象中直接调用目标对象绕过了TransactionInterceptor。示例ServicepublicclassOrderService{// ❌ 错误methodA 调用 methodB 不走代理publicvoidmethodA(){this.methodB();// this 指向目标对象非代理对象}TransactionalpublicvoidmethodB(){orderDao.insert(order);}}三种解决方案方案代码示例适用场景注入自身代理Autowired private OrderService self; self.methodB();最常用简单直接AopContext((OrderService) AopContext.currentProxy()).methodB();需配置EnableAspectJAutoProxy(exposeProxy true)拆分到新 Bean将methodB移到OrderService2中注入调用彻底解耦但增加类数量// ✅ 方案一注入自身代理推荐ServicepublicclassOrderService{AutowiredprivateOrderServiceself;// 注入的是代理对象publicvoidmethodA(){self.methodB();// 通过代理调用事务生效}TransactionalpublicvoidmethodB(){orderDao.insert(order);}}2. 异常处理导致的失效3 种场景2.1 异常被 try-catch 吞掉原因TransactionInterceptor通过捕获方法抛出的异常来决定回滚。如果异常在方法内部被 catch 且未重新抛出拦截器感知不到异常会正常提交事务。示例TransactionalpublicvoidaddUser(){try{userDao.insert(user);inti1/0;// 抛出 ArithmeticException}catch(Exceptione){log.error(异常,e);// ❌ 异常被吞掉事务正常提交}}解决方案TransactionalpublicvoidaddUser(){try{userDao.insert(user);inti1/0;}catch(Exceptione){log.error(异常,e);// ✅ 手动标记回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();// 或重新抛出throw new RuntimeException(e);}}2.2 抛出 checked Exception默认不回滚原因TransactionInterceptor的默认回滚规则只针对RuntimeException和Error。checked Exception如IOException、SQLException默认不会触发回滚。// TransactionInterceptor 源码中的回滚判断protectedvoidcompleteTransactionAfterThrowing(TransactionInfotxInfo,Throwableex){if(txInfo.transactionAttribute!nulltxInfo.transactionAttribute.rollbackOn(ex)){// 回滚}else{// 提交}}// DefaultTransactionAttribute 的默认实现publicbooleanrollbackOn(Throwableex){return(exinstanceofRuntimeException||exinstanceofError);}示例Transactional// ❌ 默认配置IOException 不会回滚publicvoidaddUser()throwsIOException{userDao.insert(user);thrownewIOException(IO 异常);// checked 异常事务提交}解决方案Transactional(rollbackForException.class)// ✅ 显式配置回滚所有异常publicvoidaddUser()throwsIOException{userDao.insert(user);thrownewIOException(IO 异常);// 现在会回滚}2.3 错误配置 noRollbackFor原因noRollbackFor显式排除了某些异常类型的回滚如果配置不当本应回滚的异常被排除。Transactional(noRollbackForRuntimeException.class)// ❌ 危险配置publicvoidaddUser(){userDao.insert(user);thrownewRuntimeException(异常);// 不会回滚}3. 配置与基础设施导致的失效4 种场景3.1 未启用事务管理EnableTransactionManagement未添加或事务管理器未配置// ❌ 错误缺少 EnableTransactionManagementConfigurationpublicclassAppConfig{BeanpublicDataSourceTransactionManagertransactionManager(DataSourceds){returnnewDataSourceTransactionManager(ds);}}// ✅ 正确ConfigurationEnableTransactionManagement// 必须添加publicclassAppConfig{BeanpublicPlatformTransactionManagertransactionManager(DataSourceds){returnnewDataSourceTransactionManager(ds);}}3.2 多数据源未指定事务管理器原因当存在多个PlatformTransactionManagerBean 时Spring 不知道使用哪个可能选择错误的或默认的事务管理器。// ❌ 错误有两个数据源但未指定用哪个事务管理器Transactional// 可能使用了错误的事务管理器publicvoidaddUser(){...}// ✅ 正确显式指定Transactional(transactionManagerorderTransactionManager)publicvoidaddUser(){...}3.3 数据库引擎不支持事务原因MySQL 的 MyISAM 引擎不支持事务即使 Spring 事务配置正确数据库层面也不会回滚。-- ❌ MyISAM 不支持事务CREATETABLEuser(...)ENGINEMyISAM;-- ✅ InnoDB 支持事务CREATETABLEuser(...)ENGINEInnoDB;3.4 异步方法Async导致事务上下文丢失原因Async会在新线程执行方法Spring 事务通过ThreadLocal绑定连接新线程没有父线程的事务上下文。TransactionalpublicvoidparentMethod(){userDao.insert(user);asyncService.asyncMethod();// ❌ 新线程事务上下文丢失}AsyncpublicvoidasyncMethod(){orderDao.insert(order);// 不在 parentMethod 的事务中}解决方案在异步方法内部单独加Transactional或使用分布式事务框架。4. 传播行为导致的伪失效2 种场景4.1 NOT_SUPPORTED 挂起事务TransactionalpublicvoidmethodA(){// 在事务 T1 中serviceB.methodB();// T1 被挂起methodB 非事务执行}Transactional(propagationPropagation.NOT_SUPPORTED)publicvoidmethodB(){orderDao.insert(order);// 非事务执行不会回滚}这不是真正的失效而是传播行为的预期效果但开发者容易误以为是事务失效。4.2 NEVER 强制无事务TransactionalpublicvoidmethodA(){serviceB.methodB();// ❌ 抛出 IllegalTransactionStateException}Transactional(propagationPropagation.NEVER)publicvoidmethodB(){orderDao.insert(order);}5. 全场景失效速查表失效场景根本原因检测方法解决方案非 public 方法AbstractFallbackTransactionAttributeSource过滤非 public检查方法修饰符改为publicfinal 方法CGLIB 无法重写 final 方法检查final修饰符移除finalstatic 方法静态方法不属于实例代理无法拦截检查static修饰符移除static未纳入 Spring 容器new创建的对象无代理检查对象来源通过Autowired注入同类 this 自调用直接调用目标对象绕过代理检查调用方式注入自身代理 /AopContext/ 拆分到新 Bean异常被 try-catch 吞掉拦截器感知不到异常检查异常处理逻辑catch 后重新抛出或手动setRollbackOnly()checked 异常默认不回滚rollbackOn()只判断 RuntimeException/Error检查异常类型和rollbackFor配置配置rollbackFor Exception.classnoRollbackFor 配置错误显式排除了应回滚的异常检查noRollbackFor配置修正配置未加 EnableTransactionManagementAOP 事务代理未启用检查启动类/配置类添加注解多数据源未指定管理器Spring 选择错误的事务管理器检查transactionManager配置显式指定transactionManager数据库引擎不支持MyISAM 等引擎无事务能力检查表引擎改用 InnoDBAsync 新线程ThreadLocal 事务上下文丢失检查异步调用异步方法内单独加事务或改用分布式事务NOT_SUPPORTED/NEVER传播行为预期效果检查传播行为配置根据业务语义调整6. 生产环境排查事务问题的 4 步法步骤操作工具/命令Step 1确认代理是否生效检查对象是否为代理对象AopUtils.isAopProxy(userService)Step 2确认事务属性是否解析检查方法是否有 TransactionAttributeTransactionAspectSupport.currentTransactionStatus()Step 3确认异常是否传播到拦截器检查异常是否被 catch 吞掉断点调试TransactionInterceptor.invoke()Step 4确认数据库层面是否回滚检查数据库引擎和连接 autoCommitSHOW CREATE TABLE user;/ 查看连接属性7. 面试官追问与高分回答模板追问 1“Spring 事务什么时候会失效请尽可能多说几种。”低分回答“非 public 方法、同类方法自调用、异常被 catch。”只说了最常见的3种不够全面高分回答 “Spring 事务失效可以从代理机制、异常处理、配置基础设施、传播行为四个维度分类 1.代理机制失效方法非 publicAbstractFallbackTransactionAttributeSource过滤、final/static 方法CGLIB 无法重写、Bean 未被 Spring 容器管理new创建、同类 this 自调用绕过代理。 2.异常处理失效异常被 try-catch 吞掉拦截器感知不到、抛出 checked Exception默认不回滚需配置rollbackFor、错误配置noRollbackFor。 3.配置基础设施失效未加EnableTransactionManagement、多数据源未指定事务管理器、数据库引擎不支持如 MyISAM、Async新线程丢失 ThreadLocal 上下文。 4.传播行为伪失效NOT_SUPPORTED挂起事务、NEVER强制无事务。 生产环境中最常见的是同类自调用和异常被吞掉这两个坑。”追问 2“同类方法自调用为什么事务会失效怎么解决”高分回答 “根本原因是Spring AOP 基于代理实现。this.methodB()中的this指向的是目标对象原始 Bean 实例而非 Spring 容器创建的代理对象。事务逻辑TransactionInterceptor的开启、提交、回滚都编织在代理对象中直接调用目标对象完全绕过了拦截器。 解决方案有三种 1.注入自身代理对象Autowired private OrderService self;然后self.methodB()最常用 2.使用AopContext.currentProxy()获取当前代理对象后调用需配置EnableAspectJAutoProxy(exposeProxy true) 3.拆分到另一个 Bean将methodB提取到OrderService2中通过Autowired注入调用彻底解耦。”追问 3“为什么 checked 异常默认不回滚什么情况下需要配置 rollbackFor”高分回答 “Spring 的默认回滚规则在DefaultTransactionAttribute.rollbackOn()中定义只回滚RuntimeException和Error。这是设计选择------RuntimeException代表编程错误或不可恢复异常应该回滚而 checked Exception如IOException代表业务异常业务层可能已经做了补偿处理强制回滚可能破坏业务逻辑。 但在实际开发中很多业务异常继承自Exceptionchecked如果希望这些异常也触发回滚必须显式配置Transactional(rollbackFor Exception.class)。 另一个常见坑是即使配置了rollbackFor如果异常在方法内被 try-catch 吞掉未抛出拦截器依然感知不到事务会正常提交。”追问 4“如果事务方法里调用了 Async 异步方法异步方法里的操作会参与事务吗”高分回答 “不会参与。Spring 事务通过TransactionSynchronizationManager中的ThreadLocal绑定数据库连接Async会在新线程中执行方法新线程没有父线程的 ThreadLocal 上下文因此异步方法完全独立于父事务。 如果异步操作需要事务必须在异步方法内部单独加Transactional。如果要求异步操作随父事务回滚必须使用分布式事务框架如 Seata或者改为同步执行。”追问 5“怎么快速排查生产环境中的事务失效问题”高分回答 “我通常按四步法排查 1.确认代理是否生效打印AopUtils.isAopProxy(service)如果返回 false说明 Bean 未被代理可能是new创建或配置错误。 2.确认事务属性是否解析在方法内调用TransactionAspectSupport.currentTransactionStatus()如果抛出异常说明事务未开启。 3.确认异常是否传播到拦截器在TransactionInterceptor.invoke()打断点看异常是否到达completeTransactionAfterThrowing()方法。 4.确认数据库层面是否回滚检查表引擎SHOW CREATE TABLE和连接的autoCommit状态。 另外开启 Spring 事务 DEBUG 日志logging.level.org.springframework.transactionDEBUG可以看到完整的事务开启、提交、回滚日志。”追问 6“final 方法为什么事务会失效static 方法呢”高分回答 “两者原因不同 -final 方法Spring 默认使用 CGLIB 代理当目标类没有实现接口时CGLIB 通过生成目标类的子类并重写父类方法来插入事务逻辑。final方法无法被重写因此代理对象无法拦截事务失效。 -static 方法静态方法属于类级别不绑定到实例对象。CGLIB 代理的是实例对象通过MethodInterceptor.intercept()拦截实例方法调用无法拦截静态方法。即使使用 JDK 动态代理基于接口接口中也不允许定义 static 方法Java 8 之前。 解决方案都是移除final/static修饰符或将逻辑移到实例方法中。”8. 方案选型速查表问题类型推荐排查/解决方案优先级事务完全没生效检查EnableTransactionManagement、方法是否为 public、Bean 是否被容器管理P0异常未触发回滚检查异常是否被 catch、是否为 checked 异常、rollbackFor配置P0部分方法无事务检查同类自调用、final/static 修饰、传播行为配置P1异步方法事务异常检查Async与Transactional的线程上下文P1偶发性数据不一致检查数据库引擎MyISAM vs InnoDB、多数据源事务管理器P1面试官想要的满分总结Spring 事务失效的本质是AOP 代理机制被打破或事务拦截链路被绕过。理解失效场景必须从源码级抓住三个核心代理是前提只有经过代理对象调用的 public 实例方法才会被TransactionInterceptor拦截。同类自调用this、final/static 方法、非容器管理的 Bean 都绕过了代理。异常是信号TransactionInterceptor通过捕获异常触发回滚。异常被 try-catch 吞掉、checked 异常默认不回滚、错误配置noRollbackFor都会导致该回滚的没回滚。上下文是纽带事务通过ThreadLocal绑定连接Async新线程会丢失上下文多数据源需要显式指定事务管理器数据库引擎MyISAM不支持事务则 Spring 事务完全无效。生产环境排查事务问题记住四步法确认代理生效 → 确认事务属性解析 → 确认异常传播到拦截器 → 确认数据库层面支持。同时开启 DEBUG 日志是最有效的定位手段。最后同类自调用和异常被吞掉是实际开发中最隐蔽、最高频的两个坑必须形成肌肉记忆。觉得对您有帮助麻烦点点关注啦您的关注是我创作的最大动力~
【大白话说Java面试题 第160题】【06_Spring篇】第20题:Spring 事务什么时候会失效?
PDF大白话说Java面试题 — 06_Spring篇第20题Spring 事务什么时候会失效回答核心考点Spring 事务失效是面试中最高频的考点之一大厂面试官不会满足于背诵非 public、同类调用等表面原因而是期望你深入理解AOP 代理机制的本质JDK 动态代理 vs CGLIB 的字节码重写边界、事务拦截的完整链路从TransactionInterceptor到ThreadLocal绑定、异常回滚的精确语义默认仅回滚 RuntimeException/Error以及传播行为对事务边界的影响。面试官真正想判断的是你是否能从源码级理解为什么失效以及能否在生产环境中快速定位和修复事务问题。1. 代理机制导致的失效4 种场景Spring 声明式事务基于 AOP 代理实现任何导致代理无法生效的情况都会使事务失效。1.1 方法非 public原因TransactionInterceptor通过 AOP 代理拦截方法调用而 JDK 动态代理只能代理接口的 public 方法CGLIB 代理虽然可以代理类但 Spring 的事务属性解析器AbstractFallbackTransactionAttributeSource明确过滤了非 public 方法// AbstractFallbackTransactionAttributeSource 源码protectedTransactionAttributecomputeTransactionAttribute(Methodmethod,Class?targetClass){// 只允许 public 方法if(allowPublicMethodsOnly()!Modifier.isPublic(method.getModifiers())){returnnull;// 非 public 方法直接返回 null不生成事务属性}// ...}示例ServicepublicclassUserService{TransactionalprivatevoidaddUser(){// ❌ 事务失效userDao.insert(user);}}解决方案将方法改为public。[citation:0]1.2 方法被 final/static 修饰原因final 方法CGLIB 代理通过生成子类并重写父类方法实现代理final方法无法被重写代理对象无法插入事务逻辑。static 方法静态方法属于类而非实例CGLIB 代理的是实例对象无法拦截静态方法调用。示例ServicepublicclassUserService{TransactionalpublicfinalvoidaddUser(){// ❌ final 方法事务失效userDao.insert(user);}TransactionalpublicstaticvoiddeleteUser(){// ❌ static 方法事务失效userDao.delete(id);}}解决方案移除final/static修饰符。[citation:1]1.3 Bean 未被 Spring 容器管理原因Spring 事务依赖 IoC 容器创建代理对象。通过new关键字创建的对象是普通 Java 对象没有经过BeanPostProcessor处理不会生成事务代理。示例// ❌ 错误new 出来的对象没有代理UserServiceuserServicenewUserService();userService.addUser();// 事务失效// ✅ 正确从 Spring 容器获取代理对象AutowiredprivateUserServiceuserService;userService.addUser();// 事务生效1.4 同类方法自调用this 调用原因this.methodB()调用的是目标对象本身的方法而非代理对象的方法。事务逻辑开启、提交、回滚编织在代理对象中直接调用目标对象绕过了TransactionInterceptor。示例ServicepublicclassOrderService{// ❌ 错误methodA 调用 methodB 不走代理publicvoidmethodA(){this.methodB();// this 指向目标对象非代理对象}TransactionalpublicvoidmethodB(){orderDao.insert(order);}}三种解决方案方案代码示例适用场景注入自身代理Autowired private OrderService self; self.methodB();最常用简单直接AopContext((OrderService) AopContext.currentProxy()).methodB();需配置EnableAspectJAutoProxy(exposeProxy true)拆分到新 Bean将methodB移到OrderService2中注入调用彻底解耦但增加类数量// ✅ 方案一注入自身代理推荐ServicepublicclassOrderService{AutowiredprivateOrderServiceself;// 注入的是代理对象publicvoidmethodA(){self.methodB();// 通过代理调用事务生效}TransactionalpublicvoidmethodB(){orderDao.insert(order);}}2. 异常处理导致的失效3 种场景2.1 异常被 try-catch 吞掉原因TransactionInterceptor通过捕获方法抛出的异常来决定回滚。如果异常在方法内部被 catch 且未重新抛出拦截器感知不到异常会正常提交事务。示例TransactionalpublicvoidaddUser(){try{userDao.insert(user);inti1/0;// 抛出 ArithmeticException}catch(Exceptione){log.error(异常,e);// ❌ 异常被吞掉事务正常提交}}解决方案TransactionalpublicvoidaddUser(){try{userDao.insert(user);inti1/0;}catch(Exceptione){log.error(异常,e);// ✅ 手动标记回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();// 或重新抛出throw new RuntimeException(e);}}2.2 抛出 checked Exception默认不回滚原因TransactionInterceptor的默认回滚规则只针对RuntimeException和Error。checked Exception如IOException、SQLException默认不会触发回滚。// TransactionInterceptor 源码中的回滚判断protectedvoidcompleteTransactionAfterThrowing(TransactionInfotxInfo,Throwableex){if(txInfo.transactionAttribute!nulltxInfo.transactionAttribute.rollbackOn(ex)){// 回滚}else{// 提交}}// DefaultTransactionAttribute 的默认实现publicbooleanrollbackOn(Throwableex){return(exinstanceofRuntimeException||exinstanceofError);}示例Transactional// ❌ 默认配置IOException 不会回滚publicvoidaddUser()throwsIOException{userDao.insert(user);thrownewIOException(IO 异常);// checked 异常事务提交}解决方案Transactional(rollbackForException.class)// ✅ 显式配置回滚所有异常publicvoidaddUser()throwsIOException{userDao.insert(user);thrownewIOException(IO 异常);// 现在会回滚}2.3 错误配置 noRollbackFor原因noRollbackFor显式排除了某些异常类型的回滚如果配置不当本应回滚的异常被排除。Transactional(noRollbackForRuntimeException.class)// ❌ 危险配置publicvoidaddUser(){userDao.insert(user);thrownewRuntimeException(异常);// 不会回滚}3. 配置与基础设施导致的失效4 种场景3.1 未启用事务管理EnableTransactionManagement未添加或事务管理器未配置// ❌ 错误缺少 EnableTransactionManagementConfigurationpublicclassAppConfig{BeanpublicDataSourceTransactionManagertransactionManager(DataSourceds){returnnewDataSourceTransactionManager(ds);}}// ✅ 正确ConfigurationEnableTransactionManagement// 必须添加publicclassAppConfig{BeanpublicPlatformTransactionManagertransactionManager(DataSourceds){returnnewDataSourceTransactionManager(ds);}}3.2 多数据源未指定事务管理器原因当存在多个PlatformTransactionManagerBean 时Spring 不知道使用哪个可能选择错误的或默认的事务管理器。// ❌ 错误有两个数据源但未指定用哪个事务管理器Transactional// 可能使用了错误的事务管理器publicvoidaddUser(){...}// ✅ 正确显式指定Transactional(transactionManagerorderTransactionManager)publicvoidaddUser(){...}3.3 数据库引擎不支持事务原因MySQL 的 MyISAM 引擎不支持事务即使 Spring 事务配置正确数据库层面也不会回滚。-- ❌ MyISAM 不支持事务CREATETABLEuser(...)ENGINEMyISAM;-- ✅ InnoDB 支持事务CREATETABLEuser(...)ENGINEInnoDB;3.4 异步方法Async导致事务上下文丢失原因Async会在新线程执行方法Spring 事务通过ThreadLocal绑定连接新线程没有父线程的事务上下文。TransactionalpublicvoidparentMethod(){userDao.insert(user);asyncService.asyncMethod();// ❌ 新线程事务上下文丢失}AsyncpublicvoidasyncMethod(){orderDao.insert(order);// 不在 parentMethod 的事务中}解决方案在异步方法内部单独加Transactional或使用分布式事务框架。4. 传播行为导致的伪失效2 种场景4.1 NOT_SUPPORTED 挂起事务TransactionalpublicvoidmethodA(){// 在事务 T1 中serviceB.methodB();// T1 被挂起methodB 非事务执行}Transactional(propagationPropagation.NOT_SUPPORTED)publicvoidmethodB(){orderDao.insert(order);// 非事务执行不会回滚}这不是真正的失效而是传播行为的预期效果但开发者容易误以为是事务失效。4.2 NEVER 强制无事务TransactionalpublicvoidmethodA(){serviceB.methodB();// ❌ 抛出 IllegalTransactionStateException}Transactional(propagationPropagation.NEVER)publicvoidmethodB(){orderDao.insert(order);}5. 全场景失效速查表失效场景根本原因检测方法解决方案非 public 方法AbstractFallbackTransactionAttributeSource过滤非 public检查方法修饰符改为publicfinal 方法CGLIB 无法重写 final 方法检查final修饰符移除finalstatic 方法静态方法不属于实例代理无法拦截检查static修饰符移除static未纳入 Spring 容器new创建的对象无代理检查对象来源通过Autowired注入同类 this 自调用直接调用目标对象绕过代理检查调用方式注入自身代理 /AopContext/ 拆分到新 Bean异常被 try-catch 吞掉拦截器感知不到异常检查异常处理逻辑catch 后重新抛出或手动setRollbackOnly()checked 异常默认不回滚rollbackOn()只判断 RuntimeException/Error检查异常类型和rollbackFor配置配置rollbackFor Exception.classnoRollbackFor 配置错误显式排除了应回滚的异常检查noRollbackFor配置修正配置未加 EnableTransactionManagementAOP 事务代理未启用检查启动类/配置类添加注解多数据源未指定管理器Spring 选择错误的事务管理器检查transactionManager配置显式指定transactionManager数据库引擎不支持MyISAM 等引擎无事务能力检查表引擎改用 InnoDBAsync 新线程ThreadLocal 事务上下文丢失检查异步调用异步方法内单独加事务或改用分布式事务NOT_SUPPORTED/NEVER传播行为预期效果检查传播行为配置根据业务语义调整6. 生产环境排查事务问题的 4 步法步骤操作工具/命令Step 1确认代理是否生效检查对象是否为代理对象AopUtils.isAopProxy(userService)Step 2确认事务属性是否解析检查方法是否有 TransactionAttributeTransactionAspectSupport.currentTransactionStatus()Step 3确认异常是否传播到拦截器检查异常是否被 catch 吞掉断点调试TransactionInterceptor.invoke()Step 4确认数据库层面是否回滚检查数据库引擎和连接 autoCommitSHOW CREATE TABLE user;/ 查看连接属性7. 面试官追问与高分回答模板追问 1“Spring 事务什么时候会失效请尽可能多说几种。”低分回答“非 public 方法、同类方法自调用、异常被 catch。”只说了最常见的3种不够全面高分回答 “Spring 事务失效可以从代理机制、异常处理、配置基础设施、传播行为四个维度分类 1.代理机制失效方法非 publicAbstractFallbackTransactionAttributeSource过滤、final/static 方法CGLIB 无法重写、Bean 未被 Spring 容器管理new创建、同类 this 自调用绕过代理。 2.异常处理失效异常被 try-catch 吞掉拦截器感知不到、抛出 checked Exception默认不回滚需配置rollbackFor、错误配置noRollbackFor。 3.配置基础设施失效未加EnableTransactionManagement、多数据源未指定事务管理器、数据库引擎不支持如 MyISAM、Async新线程丢失 ThreadLocal 上下文。 4.传播行为伪失效NOT_SUPPORTED挂起事务、NEVER强制无事务。 生产环境中最常见的是同类自调用和异常被吞掉这两个坑。”追问 2“同类方法自调用为什么事务会失效怎么解决”高分回答 “根本原因是Spring AOP 基于代理实现。this.methodB()中的this指向的是目标对象原始 Bean 实例而非 Spring 容器创建的代理对象。事务逻辑TransactionInterceptor的开启、提交、回滚都编织在代理对象中直接调用目标对象完全绕过了拦截器。 解决方案有三种 1.注入自身代理对象Autowired private OrderService self;然后self.methodB()最常用 2.使用AopContext.currentProxy()获取当前代理对象后调用需配置EnableAspectJAutoProxy(exposeProxy true) 3.拆分到另一个 Bean将methodB提取到OrderService2中通过Autowired注入调用彻底解耦。”追问 3“为什么 checked 异常默认不回滚什么情况下需要配置 rollbackFor”高分回答 “Spring 的默认回滚规则在DefaultTransactionAttribute.rollbackOn()中定义只回滚RuntimeException和Error。这是设计选择------RuntimeException代表编程错误或不可恢复异常应该回滚而 checked Exception如IOException代表业务异常业务层可能已经做了补偿处理强制回滚可能破坏业务逻辑。 但在实际开发中很多业务异常继承自Exceptionchecked如果希望这些异常也触发回滚必须显式配置Transactional(rollbackFor Exception.class)。 另一个常见坑是即使配置了rollbackFor如果异常在方法内被 try-catch 吞掉未抛出拦截器依然感知不到事务会正常提交。”追问 4“如果事务方法里调用了 Async 异步方法异步方法里的操作会参与事务吗”高分回答 “不会参与。Spring 事务通过TransactionSynchronizationManager中的ThreadLocal绑定数据库连接Async会在新线程中执行方法新线程没有父线程的 ThreadLocal 上下文因此异步方法完全独立于父事务。 如果异步操作需要事务必须在异步方法内部单独加Transactional。如果要求异步操作随父事务回滚必须使用分布式事务框架如 Seata或者改为同步执行。”追问 5“怎么快速排查生产环境中的事务失效问题”高分回答 “我通常按四步法排查 1.确认代理是否生效打印AopUtils.isAopProxy(service)如果返回 false说明 Bean 未被代理可能是new创建或配置错误。 2.确认事务属性是否解析在方法内调用TransactionAspectSupport.currentTransactionStatus()如果抛出异常说明事务未开启。 3.确认异常是否传播到拦截器在TransactionInterceptor.invoke()打断点看异常是否到达completeTransactionAfterThrowing()方法。 4.确认数据库层面是否回滚检查表引擎SHOW CREATE TABLE和连接的autoCommit状态。 另外开启 Spring 事务 DEBUG 日志logging.level.org.springframework.transactionDEBUG可以看到完整的事务开启、提交、回滚日志。”追问 6“final 方法为什么事务会失效static 方法呢”高分回答 “两者原因不同 -final 方法Spring 默认使用 CGLIB 代理当目标类没有实现接口时CGLIB 通过生成目标类的子类并重写父类方法来插入事务逻辑。final方法无法被重写因此代理对象无法拦截事务失效。 -static 方法静态方法属于类级别不绑定到实例对象。CGLIB 代理的是实例对象通过MethodInterceptor.intercept()拦截实例方法调用无法拦截静态方法。即使使用 JDK 动态代理基于接口接口中也不允许定义 static 方法Java 8 之前。 解决方案都是移除final/static修饰符或将逻辑移到实例方法中。”8. 方案选型速查表问题类型推荐排查/解决方案优先级事务完全没生效检查EnableTransactionManagement、方法是否为 public、Bean 是否被容器管理P0异常未触发回滚检查异常是否被 catch、是否为 checked 异常、rollbackFor配置P0部分方法无事务检查同类自调用、final/static 修饰、传播行为配置P1异步方法事务异常检查Async与Transactional的线程上下文P1偶发性数据不一致检查数据库引擎MyISAM vs InnoDB、多数据源事务管理器P1面试官想要的满分总结Spring 事务失效的本质是AOP 代理机制被打破或事务拦截链路被绕过。理解失效场景必须从源码级抓住三个核心代理是前提只有经过代理对象调用的 public 实例方法才会被TransactionInterceptor拦截。同类自调用this、final/static 方法、非容器管理的 Bean 都绕过了代理。异常是信号TransactionInterceptor通过捕获异常触发回滚。异常被 try-catch 吞掉、checked 异常默认不回滚、错误配置noRollbackFor都会导致该回滚的没回滚。上下文是纽带事务通过ThreadLocal绑定连接Async新线程会丢失上下文多数据源需要显式指定事务管理器数据库引擎MyISAM不支持事务则 Spring 事务完全无效。生产环境排查事务问题记住四步法确认代理生效 → 确认事务属性解析 → 确认异常传播到拦截器 → 确认数据库层面支持。同时开启 DEBUG 日志是最有效的定位手段。最后同类自调用和异常被吞掉是实际开发中最隐蔽、最高频的两个坑必须形成肌肉记忆。觉得对您有帮助麻烦点点关注啦您的关注是我创作的最大动力~