当前位置: 首页 > 科技观察

Spring事务、异步和循环依赖之间的关系是什么?

时间:2023-03-15 13:04:57 科技观察

前言循环依赖之间有一种循环依赖,就是自我注入:自力更生。Spring自调用事务时事务自注入失败。你是怎么解决的?有小伙伴建议可以注入自己来解决交易失败。具体用法如下:@Slf4j@ServicepublicclassOrderBizServiceImplimplementsOrderBizService{//自己注入@AutowiredprivateOrderBizServiceorderBizService;@OverridepublicvoidcallBack()throwsException{//一系列逻辑//需要事务操作更新订单和用户金额orderBizService.updateOrderStatusAndUserBalance();}@Override@Transactional(rollbackFor=Exception.class)publicvoidupdateOrderStatusAndUserBalance()throwsException{//内部业务逻辑}}是不是发现了什么神奇的事情,交易生效了。其实这里注入自己其实就是注入一个代理对象,调整事务,也是代理对象的事务,所以事务生效。Spring事务失败的原因:事务只有应用于公共方法才能有效;事务需要从外部调用,Spring自调用会失败;建议在实现类中一般添加事务注解@Transactional。异步自注入发现@Transactional注解可以自注入解决事务失败的问题。在某开发中,自然会想到@Async异步是否也可以自注入来解决循环依赖的问题。NO、NO、NO……事实告诉我们,不可能!从报错开始:doCreateBean抛出异常的部分开始往回推。ExposedObject==bean是这部分的问题。也就是说,当是异步的时候,再次从二级缓存中获取的和最初的是不一样的。ObjectearlySingletonReference=getSingleton(beanName,false);再次从二级缓存中获取Bean,发现这次不一样,于是报错。然后启动调试。按照循环依赖的逻辑,在执行populateBean的时候,给属性赋值,发现对自己有依赖,这时候就会创建自己。执行singleton.getObject方法getEarlyBeanReferencegetBeanPostProcessors(),此时执行getEarlyBeanReference先判断InfrastructureAdvisorAutoProxyCreator为true,调用wrapIfNecessary判断是否生成代理对象,这里并没有生成代理对象。然后开始执行异步的AsyncAnnotationBeanPostProcessor,判断为false。所以没有异步生成代理对象的逻辑。然后继续看到这一步正常进入initializeBean的逻辑,有一个部分叫applyBeanPostProcessorsAfterInitialization,所以贴出代码关键字。IDEA使用?+Shift+F进行搜索。applyBeanPostProcessorsAfterInitialization循环执行postprocessor:发现AsyncAnnotationBeanPostProcessor的PostProcessor执行完后,对象发生了变化。这样一来,二级缓存就和现在的Bean不一样了。以上就是为什么不能@Async自调用的原因,因为对象在后期的初始化阶段被proxy修改了。@Transactional为什么可能?getEarlyBeanReferencegetBeanPostProcessors()首先判断InfrastructureAdvisorAutoProxyCreator为true生成代理对象。生成代理对象事务的处理器PersistenceExceptionTranslationPostProcessor也没有被执行。继续Debug,注意applyBeanPostProcessorsAfterInitialization执行结束,发现Bean没有变化。总结@Transactional:循环依赖从二级缓存升级到三级缓存时已经生成了代理对象。@Async:在初始化阶段(initializeBean)生成代理对象。然后@Async导致后续判断exposedObject==bean为false,从而抛出异常。从注入可以看出图中有两个地方会执行BeanPostProcessor:在singletonFactory.getObject中,如果是SmartInstantiationAwareBeanPostProcessor的子类,就会执行getEarlyBeanReference方法。在initializeBean的applyBeanPostProcessorsAfterInitialization时,会执行BeanPostProcessor的所有postProcessAfterInitialization方法。后处理器执行的地方还有applyBeanPostProcessorsBeforeInitialization等,这里重点介绍这两个地方。这两个地方都可能产生代理对象。@Transactional是在getEarlyBeanReference时生成的代理对象,所以后面判断Bean是否变化时为真,@Async后面异步生成代理对象,所以判断失败。本文转载自微信公众号“程序员小航”,可通过以下二维码关注。转载本文请联系程序员小航公众号。