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

createh52周前 (03-04)技术教程2

前言

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

事务的自注入

在 Spring 自调用事务失效,你是怎么解决的? 有小伙伴提出可以自己注入自己来解决事务失效。

具体使用方式如下:

@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 {
        // 内部是事务逻辑
    }
}

是不是发现很神奇的事情,事务生效了。

其实这里注入自己,其实是注入的一个代理对象,调事务,也是调的代理对象的事务,所以事务生效。

Spring 事务失效原因:

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

异步的自注入

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

NO, NO, NO……

事实告诉我们是不可以的!

从错误开始着手:

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

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

Object earlySingletonReference = getSingleton(beanName, false);

这一次获取的时候发现不同所以报错。

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

执行 singleton.getObject 方法

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

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

那就继续往下看

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

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

循环执行后置处理器:

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

以上也就是为什么 @Async 自调用不可以,因为在后面初始化阶段被代理修改了对象。

@Transactional 为什么可以呢?

先判断 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 是在后面异步生成了代理对象,所以判断不通过。

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

作者:刘志航

出处:https://www.cnblogs.com/liuzhihang/p/spring-trans-async.html

相关文章

解决maven依赖冲突,这篇就够了

一、前言什么是依赖冲突依赖冲突是指项目依赖的某一个jar包,有多个不同的版本,因而造成了包版本冲突。依赖冲突的原因我们在maven项目的pom中 一般会引用许许多多的dependency。例如,项目A...

Java:Spring用三级缓存解决循环依赖问题

缓存其实就是三个Map容器:singletonObjects, 一级缓存;earlySingletonObjects, 二级缓存;singletonFactories 三级缓存。如下图例如:A引用B...

大厂面试必问之Spring的循环依赖

面试的重点,大厂必问之一:循环依赖1. 什么是循环依赖看下图image.png??上图是循环依赖的三种情况,虽然方式有点不一样,但是循环依赖的本质是一样的,就你的完整创建要依赖与我,我的完整创建也依赖...

Java项目中启动报错循环依赖问题解决

项目启动时候失败,有时候经常看到这样的错误信息:Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationExceptio...

彻底搞懂Spring依赖注入(一)Bean实例创建过程

上一章介绍了Bean的加载过程(IOC初始化过程),加载完成后,紧接着就要用到它的依赖注入(IOC 依赖注入)。那什么是依赖注入呢?所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到...

Spring之IOC&依赖注入

0x01、Spring1什么是Spring Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的(解耦)。? 框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时...