为何相同场景下Spring @Transactional自调用部分服务生效部分失效?
这种“一个事务生效、另一个被忽略”的差异,核心源于Spring事务实现机制的不同,常见原因有以下几种:
1. 代理方式差异:AspectJ织入 vs 动态代理(JDK/CGLIB)
Spring事务默认采用动态代理(JDK代理或CGLIB代理),这种模式下只有通过代理对象调用方法才会触发事务逻辑,自调用(this.xxx())会直接绕过代理,导致@Transactional注解失效。但如果某类被配置为AspectJ字节码增强,事务逻辑会直接织入方法的字节码中,无论调用方式是代理调用还是自调用,注解都会生效。
比如你的ShopManager可能被AspectJ织入处理(比如项目开启了<tx:annotation-driven mode="aspectj"/>,或用@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)配置),而ItemManager仍使用默认的动态代理,就会出现这种差异。
2. 某Service使用了代理对象自调用
如果某个Service内部注入了自身的代理对象,而非直接用this调用方法,就能触发事务逻辑。比如ShopManager可能有这样的实现:
@Service public class ShopManager { @Autowired ShopRepository shopRepository; // 注入自身的代理实例 @Autowired private ShopManager self; public void createShop(List<Shop> shops){ // ...业务逻辑 // 通过代理对象调用,而非this self.createAllShops(shops); } @Transactional public void createAllShops(List<Shop> shops){ shopRepository.saveAll(shops); } }
这种情况下,self.createAllShops是通过代理对象调用,事务会正常生效;而ItemManager仍用this.createAllItems自调用,绕过代理导致事务失效。
3. 事务注解的配置细节差异
虽然两个方法都标注了@Transactional,但可能存在隐性配置差异:
- 比如某方法的@Transactional指定了特殊传播属性(如
propagation = Propagation.REQUIRES_NEW),结合AspectJ织入时会生效,但动态代理下自调用仍会失效; - 或某Service的方法被Spring其他代理机制(如@Cacheable)处理过,间接触发了事务代理的生效逻辑。
4. Bean初始化顺序差异(极端场景)
极少数情况下,若某Service的代理初始化提前完成,而另一个Service的代理初始化滞后,会导致自调用时代理未生效,不过这种情况仅出现在复杂的Bean依赖场景中。
内容的提问来源于stack exchange,提问作者vinodpthmn

