spring事务传播机制回顾 Spring 事务传播机制Propagation Behavior主要解决的是‌多个带有事务的方法互相调用时事务该如何界定、继承或隔离‌的问题。它定义了当前方法在执行时是加入已有的事务、新建一个独立事务还是以非事务方式运行。Spring 官方定义了 ‌7 种‌ 传播行为以下是详细解析及最佳实践一、7 种传播行为详解传播行为核心逻辑适用场景‌REQUIRED‌(默认)‌有则加入无则新建‌。如果当前存在事务则加入该事务如果当前没有事务则创建一个新事务。‌最常用‌。适用于大多数业务场景如用户下单扣库存、减余额、写订单保证所有操作要么全成功要么全失败。‌REQUIRES_NEW‌‌挂起旧事务新建独立事务‌。无论当前是否存在事务都挂起当前事务并创建一个全新的独立事务。新事务提交或回滚不影响原事务。‌独立记录‌。适用于即使主业务失败也要成功保存的操作如记录“下单失败日志”或发送通知。‌SUPPORTS‌‌有则加入无则非事务‌。如果当前存在事务则加入如果当前没有事务则以非事务方式运行。‌纯查询‌。适用于读取操作有事务能保证一致性没事务也不影响读取避免不必要的资源消耗。‌NESTED‌‌嵌套事务保存点‌。如果当前存在事务则在嵌套事务内执行创建保存点如果当前没有事务则按 REQUIRED 处理。子事务回滚不影响主事务但主事务回滚会导致子事务一起回滚。‌部分回滚‌。适用于希望子流程失败时只回滚子流程而不影响主流程其他操作的场景需数据库支持保存点。‌MANDATORY‌‌强制要求事务‌。如果当前存在事务则加入如果当前没有事务则抛出异常。‌内部辅助方法‌。确保该方法不会被单独调用必须在事务上下文中执行。‌NOT_SUPPORTED‌‌强制非事务‌。以非事务方式执行如果当前存在事务则挂起当前事务。‌耗时非核心操作‌。适用于长时间运行且不需要事务保护的操作避免长时间占用事务资源如锁。‌NEVER‌‌绝不开启事务‌。以非事务方式执行如果当前存在事务则抛出异常。‌绝对禁止事务‌。适用于某些工具方法或特定逻辑严禁在事务环境中运行。二、开发选型指南在实际开发中无需死记硬背所有类型掌握以下三种即可覆盖 90% 的场景‌主业务流程用 REQUIRED‌这是默认值也是最常用的。它确保了业务操作的原子性。‌独立日志/通知用 REQUIRES_NEW‌当需要记录日志或发送消息且不希望这些操作随主业务回滚而消失时使用。‌纯查询用 SUPPORTS‌对于只读操作使用此模式可以避免开启不必要的事务提升性能。三、常见陷阱与注意事项在使用事务传播机制时需特别注意以下三个容易导致失效的问题‌同类方法调用不生效自调用问题‌Spring 事务是基于 ‌AOP 代理‌ 实现的。如果在同一个类中方法 A 直接调用方法 B即使方法 B 标注了不同的传播行为如 REQUIRES_NEW事务配置也会失效。因为内部调用绕过了代理对象。‌解决‌将方法 B 提取到另一个 Service 类中或通过注入自身代理对象调用。‌异常被捕获导致不回滚‌:Spring 只有在接收到‌未捕获的运行时异常‌RuntimeException或 Error 时才会触发回滚。如果在代码中使用 try-catch 捕获了异常且未重新抛出Spring 无法感知错误事务将正常提交可能导致数据不一致。‌解决‌在 catch 块中手动调用 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 或重新抛出异常。‌数据库引擎支持‌:事务生效的前提是底层数据库引擎支持事务。例如MySQL 必须使用 ‌InnoDB‌ 引擎如果使用 MyISAM 引擎配置任何传播机制都无法实现事务控制。四如何设置Spring事务的传播行为基于前面对 Spring 事务传播机制如 REQUIRED、REQUIRES_NEW 等及其核心逻辑的讨论设置传播行为主要通过 ‌Transactional 注解‌ 或 ‌XML 配置‌ 实现。最常用且推荐的方式是使用注解。一、使用 Transactional 注解推荐在 Service 层的方法或类上添加 Transactional 注解并通过 propagation 属性指定传播行为。方法级设置精准控制ServicepublicclassOrderService{AutowiredprivateOrderMapperorderMapper;AutowiredprivateLogServicelogService;// 默认传播行为REQUIREDTransactional(propagationPropagation.REQUIRED)publicvoidcreateOrder(Orderorder){orderMapper.insert(order);// 即使这里出错下面的日志记录也会独立提交logService.saveLog(Order created);}}类级设置全局默认若在类上标注则该类下所有公共方法默认使用该传播行为。ServiceTransactional(propagationPropagation.REQUIRED)publicclassUserService{// 所有方法默认都是 REQUIREDpublicvoidaddUser(){...}// 可以单独覆盖某个方法的传播行为Transactional(propagationPropagation.SUPPORTS)publicUsergetUserById(Longid){...}}五、常用传播行为枚举值在 org.springframework.transaction.annotation.Propagation 枚举中选择Propagation.REQUIRED默认值有事务则加入无则新建。Propagation.REQUIRES_NEW挂起当前事务新建独立事务。Propagation.SUPPORTS有事务则加入无则以非事务运行。Propagation.NESTED嵌套事务保存点。Propagation.MANDATORY强制要求当前存在事务。Propagation.NOT_SUPPORTED以非事务运行挂起当前事务。Propagation.NEVER以非事务运行若存在事务则抛异常。六、XML 配置方式传统项目在 applicationContext.xml 中通过 tx:advice 配置tx:advice idtxAdvicetransaction-managertransactionManagertx:attributes!--所有以 save 开头的方法使用REQUIRED--tx:method namesave*propagationREQUIRED/!--所有以 get 开头的方法使用SUPPORTS--tx:method nameget*propagationSUPPORTSread-onlytrue/!--特定方法使用REQUIRES_NEW--tx:method namesendNotificationpropagationREQUIRES_NEW//tx:attributes/tx:adviceaop:configaop:pointcut idservicePointcutexpressionexecution(* com.example.service.*.*(..))/aop:advisor advice-reftxAdvicepointcut-refservicePointcut//aop:config七、关键注意事项‌自调用失效‌如前文所述同类中方法 A 调用方法 BB 的传播行为设置会失效。因为内部调用不经过 Spring AOP 代理。‌解决‌将不同传播行为的方法拆分到不同的 Service 类中或通过注入自身代理对象调用。‌接口与实现类‌建议在‌实现类‌上添加 Transactional。若在接口上添加需确保使用的是 JDK 动态代理默认且接口方法必须是 public。‌只读优化‌对于查询操作SUPPORTS 或 REQUIRED建议同时设置 readOnly true有助于数据库引擎优化性能。八、总结‌核心方式‌使用 Transactional(propagation Propagation.XXX)。‌默认行为‌REQUIRED。‌独立事务‌使用 REQUIRES_NEW 并注意拆分类以避免自调用失效。‌关联前文‌设置传播行为是实现前文所述“主业务与日志分离”、“部分回滚”等场景的技术手段。