Spring碎片整理-创建Bean对象时循环依赖的解决
写在前面
我们来看一下这样的二个对象:
public class CycleReferenceBeanA {
private CycleReferenceBeanB cycleReferenceBeanB;
public CycleReferenceBeanB getCycleReferenceBeanB() {
return cycleReferenceBeanB;
}
public void setCycleReferenceBeanB(CycleReferenceBeanB cycleReferenceBeanB) {
this.cycleReferenceBeanB = cycleReferenceBeanB;
}
}
public class CycleReferenceBeanB {
private CycleReferenceBeanA cycleReferenceBeanA;
public CycleReferenceBeanA getCycleReferenceBeanA() {
return cycleReferenceBeanA;
}
public void setCycleReferenceBeanA(CycleReferenceBeanA cycleReferenceBeanA) {
this.cycleReferenceBeanA = cycleReferenceBeanA;
}
}
如果你没有学习过Spring的源码,那你很可能注意不到这一点。如果一个对象A中有B的依赖,而同时B又有A的依赖时,Spring是如何处理这种情况的呢?每个人在阅读Spring源码时都绕不过循环依赖这个关键字,接下来我们就一起看一看吧。(本文主要来自书《Spring源码深度解析》中的一个案例)
在看具体的实例之前,我们先了解一个概念:
Spring容器在创建对象的过程中会把每个对象的标识放入一个"当前创建Bean池"中(DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation),其代码片段如下:
Spring根据ObjectFactory创建实例对象的方法 --> DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory)
在创建对象时将beanName置入当前创建Bean池中 --> DefaultSingletonBeanRegistry.beforeSingletonCreation(String beanName)
有了上面"当前创建对象池的概念后,大家还要理解ObjectFactory这个概念,其详细信息大家可以百度一下自行了解。这里大概说一下,众所周知Spring在实例一个Bean的过程中,首先会根据合适的构造创建一个初步的实例对象,然后对此对象进行进一步的修饰(注入属性依赖、执行后处理器逻辑等操作),所有的操作都走完后才是一个Bean对象的完整创建流程。而ObjectFactory的概念就是就是上述的预创建的实例对象。
同样的一段Bean的创建代码,我们看一下下面的3种情况:
public class CycleReference {
public static void main(String[] args) {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("com/flash/springcode/application.xml"));
CycleReferenceBeanA a = (CycleReferenceBeanA) beanFactory.getBean("beanA");
}
}
1. 构造器循环依赖
通过构造器注入形成的循环依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示出现了循环依赖问题。
举例如下:
运行后结果:
其报错的原因较为容易理解:A和B的构造互相注入使得两对象根本无法进行基础的实例化操作,所以后续的操作无从谈起。
2. setter循环依赖
举例如下:
运行后结果:
其核心代码如下:
- 在创建对象beanA时(以下简称A),
doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
方法中,发现对象beanA有循环依赖的情况,创建此对象的ObjectFactory并且以beanName为key储存起来。
2. 在为A注入参数B时触发B的创建流程,在B的创建过程中注入参数A,此时因为beanA已经被创建会进入到getSingle()系列的方法,这里不再细说,其核心部分代码如下。
3. 如上图红标所示,因为在创建A的时候已经将对应的ObjectFactory放入置singletonFactories
中,所以此时直接返回了A的对象实例,B创建完成成功添加A的对象引用。当B创建完成后,A自然完成B的注入,A和B之间的互相引用就完成了。
3. prototype范围的循环依赖
非单例的bean对象如果有循环依赖,Spring也是无法解决的。其根本原因也不难理解。我们思考一下,单例对象的互相依赖解决的方案是根据构造函数创建一个初步的A对象后,将其先注入给B,后续将B反注入A。这个过程有一个非常关键的点:A对象在实例化之后,此对象在内存有且只有一个,而多例的Bean对象自然无法满足此条件。