Spring 循环依赖

什么是 Spring 循环依赖

循环依赖:对象 A 中有一个属性为对象 B,对象 B 中有一个属性为对象 A,如下所示:

如果不考虑 Spring 自动注入的话,循环依赖并不是问题,直接像下面这种方式初始化即可:

但如果考虑 Spring 的话,循环依赖就是一个问题,因为在 Spring 中,一个 Bean 的创建流程可以粗略的分为三个步骤:(更具体可见 Bean 的生命周期)

在完成上面三个步骤后,Spring 才会将 Bean 实例添加到缓存中 (singletonObjects,一级缓存),以后调用getBean()方法时先去缓存中找,缓存中不存在才会创建

现在模拟 Spring 创建 Bean 实例的过程:

此时就出现了死循环,用个图来更清楚表示:

1

简单循环依赖的解决方法

简单循环依赖也就是不存在 AOP 的循环依赖,只需要增加一个二级缓存 (earlySingletonObjects) 即可

上部分说一级缓存是存放完成「实例化 -> 属性赋值 -> 初始化」三个步骤的 Bean 实例,那么二级缓存就是存在只完成了「实例化」的 Bean 实例

加入二级缓存后的过程如下图所示:

2

首先会将完成实例化的 Bean 加入到二级缓存中,当 Bean 三个步骤都完成后,会将 Bean 从二级缓存中移除,并加入到一级缓存中,所以二级缓存中存放的是非完全品!!

当我们需要判断缓存是否存在 Bean 实例时,先去二级缓存中寻找,如果不存在再去一级缓存中寻找,这样就可以解决循环依赖的问题

同样的,下面模拟 Spring 创建 Bean 实例的过程:

到此为止,完美解决不存在 AOP 的循环依赖问题!!

AOP 循环依赖的解决方法

之所以存在 AOP 的循环依赖问题需要单独拿出来讨论,是因为 AOP 实际上访问的是代理对象,而代理对象是在目标对象完成初始化后执行postProcessAfterInitialization方法创建

代理对象和目标对象不是同一个对象,如果按照上面加入二级缓存的方案,那么 BeanB 实例给属性赋值的就是目标对象,但实际上应该赋值为代理对象才对,但代理对象又是在初始化后才创建,这就很难搞~

关于上述 AOP 创建代理对象的细节可见 Spring AOP 源码剖析

Spring 在存在 AOP 时又加了一层缓存,称为三级缓存 (singletonFactories)

与一级缓存和二级缓存不同的在于,三级缓存的 value 不是 Object,而是 ObjectFactory,它表示对象工厂,用来创建某个对象

在 Spring 中,创建 Bean 实例的源码主要在doCreateBean方法中:(将非主要部分省略,详细分析可见 Spring IoC)

从上面可以看到调用SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference方法,更具体是调用AbstractAutoProxyCreator中的该方法:

ObjectFactory是一个函数式接口,上面通过 lambda 表达式生成了一个对应的实现类,当调用getEarlyBeanReference方法时会创建一个代理对象,而 ObjectFactory 对象存放在三级缓存中

此时,整个流程如下图所示:

3

doCreateBean方法中可以看出,加入三级缓存也是在实例化完成之后,属性赋值之前

当从三级缓存中取,取出来的是一个ObjectFactory对象,调用创建该对象的 lambda 表达式指定的方法可以提前创建代理对象

为了避免重复创建代理对象,会将创建的代理对象存入earlyProxyReferences中,在创建代理对象前需要判断earlyProxyReferences是否存在,如果不存在才开始创建代理对象

参考文章