Spring 事务、异步和循环依赖有什么关系?

前言

在循环依赖中有一种循环依赖,就是自注入:本身依赖本身。java

事务的自注入

Spring 自调用事务失效,你是怎么解决的? 有小伙伴提出能够本身注入本身来解决事务失效。缓存

具体使用方式以下:app

@Slf4j
@Service
public class OrderBizServiceImpl implements OrderBizService {

    // 注入本身
    @Autowired
    private OrderBizService orderBizService;

    @Override
    public void callBack() throws Exception {

        // 一系列的逻辑

        // 须要事务操做更新订单和用户金额
        orderBizService.updateOrderStatusAndUserBalance();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderStatusAndUserBalance() throws Exception {
        // 内部是事务逻辑
    }
}

是否是发现很神奇的事情,事务生效了。异步

其实这里注入本身,实际上是注入的一个代理对象,调事务,也是调的代理对象的事务,因此事务生效。ide

Spring 事务失效缘由:

事务只能应用到 public 方法上才会有效;
事务须要从外部调用,Spring 自调用会失效;
建议事务注解 @Transactional 通常添加在实现类上。post

异步的自注入

发现 @Transactional 注解能够自注入解决事务失效的问题,在某次开发中,天然而然想到 @Async 异步是否是也能够自注入解决循环依赖的问题。spa

NO, NO, NO……代理

事实告诉咱们是不能够的!code

从错误开始着手:对象

抛出异常部分 doCreateBean

开始往上面反推 exposedObject == bean 是这一块出了问题。

也就是说异步的时候,再次从二级缓存中获取的和初始的不相同。

Object earlySingletonReference = getSingleton(beanName, false);

从二级缓存再次获取 Bean

这一次获取的时候发现不一样因此报错。

那就开始 Debug, 按照循环依赖的逻辑,执行到 populateBean 时,属性赋值,发现有依赖本身,此时会建立本身。

执行 singleton.getObject 方法

getEarlyBeanReference

getBeanPostProcessors()

而此时执行 getEarlyBeanReference 先判断 InfrastructureAdvisorAutoProxyCreator true 调用 wrapIfNecessary 判断是否生成一个代理对象,这里并无生成代理对象。

而后开始执行异步的 AsyncAnnotationBeanPostProcessor 判断为 false。因此没有执行异步的生成代理对象逻辑。

那就继续往下看

到这一步仍是正常的

进入到 initializeBean 的逻辑,有一部分叫作 applyBeanPostProcessorsAfterInitialization

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

applyBeanPostProcessorsAfterInitialization

循环执行后置处理器:

发现执行完 AsyncAnnotationBeanPostProcessor 这个 PostProcessor 后,对象被改变了。从而致使二级缓存和当前的 Bean 不一样。

以上也就是为何 @Async 自调用不能够,由于在后面初始化阶段被代理修改了对象。

@Transactional 为何能够呢?

getEarlyBeanReference

getBeanPostProcessors()

先判断 InfrastructureAdvisorAutoProxyCreator true 生成一个代理对象。

生成代理对象

事务的处理器 PersistenceExceptionTranslationPostProcessor 也没有执行。

继续 Debug 关注 applyBeanPostProcessorsAfterInitialization

执行结束,发现 Bean 没有发生改变。

总结

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

自注入

能够看出图中有两处会执行 BeanPostProcessor :

  1. 在 singletonFactory.getObject 时,若是是 SmartInstantiationAwareBeanPostProcessor 的子类会执行 getEarlyBeanReference 方法。
  2. 在 initializeBean 的 applyBeanPostProcessorsAfterInitialization 时会执行全部 BeanPostProcessor 的 postProcessAfterInitialization 的方法。
也有其余的地方在执行后置处理器,好比 applyBeanPostProcessorsBeforeInitialization ,只不过这里关注这俩处。

而这两处都有可能生成代理对象, @Transactional 是在 getEarlyBeanReference 处生成的代理对象,因此后面判断 Bean 是否被改变时为 true,而 @Async 是在后面异步生成了代理对象,因此判断不经过。

至此,分析完毕,错误之处,欢迎指正。

相关推荐

相关文章
相关标签/搜索