10|SpringBoot 自动配置原理 一句话讲清: 1. 启动类注解 @SpringBootApplication 2. 内部 @EnableAutoConfiguration 3. 读取 M

张开发
2026/4/16 6:37:18 15 分钟阅读

分享文章

10|SpringBoot 自动配置原理 一句话讲清: 1. 启动类注解 @SpringBootApplication 2. 内部 @EnableAutoConfiguration 3. 读取 M
Transactional 失效场景面试最爱挖的 6 个坑面试官“你遇到过Transactional失效的情况吗”你“遇到过。比如方法不是 public、同类中方法互相调用、异常被 try-catch 吞掉、传播属性设置错误、数据库引擎不支持事务、抛出了非 RuntimeException 异常等。”面试官“那你能具体说说为什么同类调用会失效吗底层原理是什么”你“……”很多人能列举失效场景但一问到“为什么”就卡壳了。本文从 Spring AOP 代理原理出发把 6 个常见失效场景彻底讲透并给出解决方案。一、Transactional失效的本质原因Spring 事务管理基于AOP 动态代理。当一个 Bean 被代理后只有通过代理对象调用方法才会触发事务增强逻辑。直接通过this调用目标对象的方法不会经过代理因此Transactional完全无效。理解了这个根本原因下面 6 个失效场景就都能解释通了。二、6 大失效场景详解场景 1方法非 public现象Transactional标注在private、protected或default访问权限的方法上事务不生效。原理Spring 默认使用 CGLIB 或 JDK 动态代理。对于 CGLIB它通过生成子类覆盖目标方法实现增强但private方法无法被子类覆盖对于 JDK 动态代理代理类只实现接口方法private方法不在接口中。Spring 会忽略非public方法的Transactional注解且不报错。ServicepublicclassOrderService{Transactional// 失效方法不是 publicprivatevoidupdateOrder(){}}解决方案确保事务方法声明为public。如果需要保护内部方法可以将其提取到单独的 Service 类中。场景 2同类中方法互相调用自调用现象同一个类中方法 A无Transactional调用方法 B有Transactional事务不生效。ServicepublicclassOrderService{publicvoidmethodA(){methodB();// 直接调用不会经过代理事务失效}TransactionalpublicvoidmethodB(){}}原理methodA通过this.methodB()调用this是原始对象不是代理对象。代理对象只有在外部调用时才会生效。解决方案注入自身代理推荐ServicepublicclassOrderService{AutowiredprivateOrderServiceselfProxy;publicvoidmethodA(){selfProxy.methodB();// 通过代理调用}}从 Spring 容器中获取代理OrderServiceproxy(OrderService)AopContext.currentProxy();proxy.methodB();将方法 B 提取到另一个 Service最清晰符合单一职责。场景 3异常被 try-catch 吃掉没有抛出现象方法内部 catch 了异常但没有重新抛出事务不会回滚。TransactionalpublicvoidupdateOrder(){try{// 执行数据库操作可能抛出异常}catch(Exceptione){log.error(异常被捕获未抛出,e);// 没有重新抛出事务会正常提交}}原理Spring 事务管理器通过检测方法抛出的异常来决定是否回滚。如果异常被捕获且未重新抛出Spring 认为方法执行成功会提交事务。解决方案要么在 catch 块中抛出异常throw e;要么手动标记事务为回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();场景 4传播属性设置错误现象使用了Propagation.NOT_SUPPORTED、Propagation.NEVER等传播行为导致事务不存在。Transactional(propagationPropagation.NOT_SUPPORTED)publicvoidmethodB(){}// 这个方法永远在非事务环境下执行如果调用方有事务会被挂起如果调用方无事务也以非事务执行。总之方法 B 内不会有事务。解决方案确认传播行为符合预期。大部分业务场景应使用默认的REQUIRED。场景 5数据库引擎不支持事务现象代码和配置都没问题但事务就是不生效。常见于 MySQL 使用 MyISAM 引擎。原理MyISAM 引擎不支持事务没有 undo log而 InnoDB 支持。如果表的存储引擎是 MyISAM无论怎么配置Transactional都不会有回滚效果。解决方案检查表引擎SHOW TABLE STATUS WHERE Name your_table;修改为 InnoDBALTER TABLE your_table ENGINEInnoDB;在创建表时指定引擎ENGINEInnoDB场景 6抛出了非 RuntimeException 异常现象方法抛出了Exception非RuntimeException子类事务没有回滚。Transactionalpublicvoidmethod()throwsException{// ...thrownewException(checked exception);}原理Spring 事务回滚的默认行为是只对RuntimeException和Error进行回滚对checked exceptionException的子类但非RuntimeException默认不进行回滚。解决方案配置rollbackFor属性Transactional(rollbackForException.class)或者抛出RuntimeException的子类。三、其他容易被忽略的失效场景7. 多数据源配置错误如果配置了多个TransactionManager但没有指定Transactional(txManagerName)Spring 可能使用了错误的事务管理器导致事务不生效。8. 事务方法所在的 Bean 没有被 Spring 管理类上没有Service、Component等注解或者通过new手动创建对象Transactional完全无效。9. 切面顺序导致事务增强被覆盖如果自定义了 AOP 切面且执行顺序在事务切面之前且切面内部 catch 了异常可能导致事务切面无法接收到异常。10. 方法内部使用了异步线程在事务方法内新开一个线程执行数据库操作新线程的数据库连接与当前事务不绑定操作不会参与当前事务。四、如何快速排查事务失效检查方法是否为 public检查是否通过代理调用排除自调用检查异常是否被吞掉看日志检查传播行为默认 REQUIRED检查数据库引擎InnoDB检查异常类型是否在 rollbackFor 范围内开启 Spring 事务日志logging.level.org.springframework.transactionDEBUG logging.level.org.springframework.orm.jpaDEBUG观察日志中事务的创建、提交、回滚信息。五、总结表失效场景根本原因解决方案方法非 public代理无法拦截非 public 方法改为 public同类自调用未通过代理对象调用注入自身代理或拆分到新类异常被 catch 吃掉Spring 未感知到异常抛出异常或手动回滚传播属性错误事务不存在如 NOT_SUPPORTED确认传播行为默认 REQUIRED数据库引擎不支持MyISAM 等不支持事务改为 InnoDB非 RuntimeException默认不回滚 checked exception配置 rollbackFor Exception.class一句话记住失效场景非 public、自调用、吞异常、传播错、引擎差、异常选错。希望这篇文章能帮你避开Transactional的常见陷阱在面试和开发中都能从容应对。如果需要进一步了解分布式事务或编程式事务欢迎继续讨论。

更多文章