前言

在循环依赖中有一种循环依赖,就是自注入:自己依赖自己。
Spring 事务和异步循环依赖的影响 - 图1

事务的自注入

Spring 自调用事务失效,你是怎么解决的? 有小伙伴提出可以自己注入自己来解决事务失效。
具体使用方式如下:

  1. @Slf4j
  2. @Service
  3. public class OrderBizServiceImpl implements OrderBizService {
  4. // 注入自己
  5. @Autowired
  6. private OrderBizService orderBizService;
  7. @Override
  8. public void callBack() throws Exception {
  9. // 一系列的逻辑
  10. // 需要事务操作更新订单和用户金额
  11. orderBizService.updateOrderStatusAndUserBalance();
  12. }
  13. @Override
  14. @Transactional(rollbackFor = Exception.class)
  15. public void updateOrderStatusAndUserBalance() throws Exception {
  16. // 内部是事务逻辑
  17. }
  18. }

是不是发现很神奇的事情,事务生效了。
其实这里注入自己,其实是注入的一个代理对象,调事务,也是调的代理对象的事务,所以事务生效。

Spring 事务失效原因: 事务只能应用到 public 方法上才会有效; 事务需要从外部调用,Spring 自调用会失效; 建议事务注解 _@_Transactional 一般添加在实现类上。

异步的自注入

发现 _@_Transactional 注解可以自注入解决事务失效的问题,在某次开发中,自然而然想到 _@_Async 异步是不是也可以自注入解决循环依赖的问题。
NO, NO, NO……
事实告诉我们是不可以的!
Spring 事务和异步循环依赖的影响 - 图2
从错误开始着手:
Spring 事务和异步循环依赖的影响 - 图3
开始往上面反推 exposedObject == bean 是这一块出了问题。
也就是说异步的时候,再次从二级缓存中获取的和初始的不相同。
Object earlySingletonReference = getSingleton(beanName, false);
Spring 事务和异步循环依赖的影响 - 图4
这一次获取的时候发现不同所以报错。
那就开始 Debug, 按照循环依赖的逻辑,执行到 populateBean 时,属性赋值,发现有依赖自己,此时会创建自己。
执行 singleton.getObject 方法
Spring 事务和异步循环依赖的影响 - 图5
Spring 事务和异步循环依赖的影响 - 图6
而此时执行 getEarlyBeanReference 先判断 InfrastructureAdvisorAutoProxyCreator true 调用 wrapIfNecessary 判断是否生成一个代理对象,这里并没有生成代理对象。
Spring 事务和异步循环依赖的影响 - 图7
然后开始执行异步的 AsyncAnnotationBeanPostProcessor 判断为 false。所以没有执行异步的生成代理对象逻辑。
那就继续往下看
Spring 事务和异步循环依赖的影响 - 图8
进入到 initializeBean 的逻辑,有一部分叫做 applyBeanPostProcessorsAfterInitialization

方面小伙伴搜索,所以贴出来代码关键字。IDEA 使用 ⌘ + Shift + F 搜索。

Spring 事务和异步循环依赖的影响 - 图9
循环执行后置处理器:
Spring 事务和异步循环依赖的影响 - 图10
Spring 事务和异步循环依赖的影响 - 图11
发现执行完 AsyncAnnotationBeanPostProcessor 这个 PostProcessor 后,对象被改变了。从而导致二级缓存和当前的 Bean 不同。
以上也就是为什么 _@_Async 自调用不可以,因为在后面初始化阶段被代理修改了对象。

_@_Transactional 为什么可以呢?

Spring 事务和异步循环依赖的影响 - 图12
Spring 事务和异步循环依赖的影响 - 图13
先判断 InfrastructureAdvisorAutoProxyCreator true 生成一个代理对象。
Spring 事务和异步循环依赖的影响 - 图14
事务的处理器 PersistenceExceptionTranslationPostProcessor 也没有执行。
继续 Debug 关注 applyBeanPostProcessorsAfterInitialization
Spring 事务和异步循环依赖的影响 - 图15
执行结束,发现 Bean 没有发生改变。

总结

  • @Transactional: 是在循环依赖从二级缓存升到三级缓存的时候已经生成了代理对象。
  • @Async: 是在初始化阶段(initializeBean)去生成代理对象。然后 _@_Async 导致后面判断 exposedObject == bean 为 false ,从而抛出异常。

Spring 事务和异步循环依赖的影响 - 图16
可以看出图中有两处会执行 BeanPostProcessor :

  1. 在 singletonFactory.getObject 时,如果是 SmartInstantiationAwareBeanPostProcessor 的子类会执行 getEarlyBeanReference 方法。
  2. 在 initializeBean 的 applyBeanPostProcessorsAfterInitialization 时会执行所有 BeanPostProcessor 的 postProcessAfterInitialization 的方法。

    也有其他的地方在执行后置处理器,比如 applyBeanPostProcessorsBeforeInitialization ,只不过这里关注这俩处。

而这两处都有可能生成代理对象, _@_Transactional 是在 getEarlyBeanReference 处生成的代理对象,所以后面判断 Bean 是否被改变时为 true,而 _@_Async 是在后面异步生成了代理对象,所以判断不通过。
至此,分析完毕,错误之处,欢迎指正。

相关推荐