在Spring 的使用的时候 可能会碰到一下的场景
<bean id="a" class="com.zhh.a">
<property name="b" ref="b"></property>
</bean>
<bean id="b" class="com.zhh.b">
<property name="a" ref="a"></property>
</bean>
a-->b 即a依赖于b , b--> a即 b依赖于a
Spring提供构造器注入和Setter注入,那么也就可能造成构造器循环依赖和setter循环依赖。
构造器注入
<bean id ="a" class="com.zhh.a">
<constructor-arg value="a" ref="b"></constructor-arg>
</bean>
<bean id ="b" class="com.zhh.b">
<constructor-arg value="b" ref="c"></constructor-arg>
</bean>
<bean id ="c" class="com.zhh.c">
<constructor-arg value="a" ref="a"></constructor-arg>
</bean>
此时构造器 形成了循环依赖
1.Spring对于构造器的循环依赖并没有解决 而是通过抛出异常
在创建A的时候发现,首先去 当前 "当前创建的Bean池",查找当前bean是否在创建,如果没有发现,则继续准备需要的构造参数B,并将A 放入到当前正在创建的Bean池。
2.创建B的时候发现,首先去 当前 "当前创建的Bean池",查找当前bean是否在创建,如果没有发现,则继续准备需要的构造参数C,并将B 放入到当前正在创建的Bean池。
3.创建C的时候发现,首先去 当前 "当前创建的Bean池",查找当前bean是否在创建,如果没有发现,则继续准备需要的构造参数A,并将A 放入到当前正在创建的Bean池
4.再次创建A的时候,发现A已近在当前创建的Bean池,表示循环依赖,则抛出beanCurrentLyInCreationException
Setter注入
1.Spring容器在创建单例“A”的时候,根据无参构造器,创建bean,并且暴露出ObjectFactory,用于返回一个提前暴露一个创建中的Bean,并将“A” 放入到当前创建的Bean池中,
2.Spring容器在创建单例“B”的时候,根据无参构造器,创建bean,并且暴露出ObjectFactory,用于返回一个提前暴露一个创建中的Bean,并将“B” 放入到当前创建的Bean池中,
3.Spring容器在创建单例“C”的时候,根据无参构造器,创建bean,并且暴露出ObjectFactory,用于返回一个提前暴露一个创建中的Bean,并将“C” 放入到当前创建的Bean池中,然后通过setter注入A,由于创建A的时候提前暴露了ObjectFactory,从而可以返回提前暴露的一个Bean.
4.最后通过依赖注入,A和B ,完成setter注入
源码分析
//beanName --> bean instance
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//beanName -->ObjectFactory 用于解决循环依赖 临时变量
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
//beanName --> bean instance 用于解决循环依赖 临时变量
private final Map<String, Object> earlySingletonObjects = new HashMap(16);
singletonFactories : 用于保存当前BeanName和 对象工厂的引用 一旦当前对象被创建singletonFactories 则BeanName 信息被删除
earlySingletonObjects :用于保存早期BeanName和bean实例的引用,也是当前Bean 一旦被创建 earlySingletonObjects 信息将被删除
下面介绍方法一
protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);//将当前Objectory 添加到singletonFactories
this.earlySingletonObjects.remove(beanName);//同时保证当前earlySingletonObjects 没有
this.registeredSingletons.add(beanName);
}
}
}
方法二
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//通过beanName获取单例bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
Map var4 = this.singletonObjects;
synchronized(this.singletonObjects) {
//通过beanName获取BeanFacotory
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject != NULL_OBJECT ? singletonObject : null;
}
方法三
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
在方法一中:对象信息对beanFactory的形式被放入singletonFactories中,这时earlySingletonObjects中肯定没有此对象(因为remove)。
在方法二中:经过过一些条件 allowEarlyReference 为true的时候,bean的信息 就从singletonFactory工厂中拿出来,被放入到earlySingletonObjects 同时 singletonFactories 调用remove 掉 也就是说earlySingletonObjects 和 singletonFactories是一个互斥的变量
在方法三中:在方法3中,对象被加入到singletonObjects中,同时singletonFactories和earlySingletonObjects中都remove掉持有的对象(不管持有与否),这就表示在之前的处理中,这只相当于一个临时容器,处理完毕之后都会remove掉。
在正常的情况下,调用顺序如下:以下有无,表示是否持有对指定Bean的引用
singletonFactories | earlySingletonObjects | singletonObjects | |
---|---|---|---|
getSingleton(beanName, true) | 无 | 无 | 无 |
doCreateBean(beanName,mdb,args) | 有 | 无 | 无 |
getSingleton(beanName, true); | 有 | 无 | 无 |
addSingleton(beanName, singletonObject) | 无 | 无 | 有 |
如果出现循环依赖 解决调用顺序如下
singletonFactories | earlySingletonObjects | singletonObjects | |
---|---|---|---|
getSingleton(A, true) | A无B无 | A无B无 | A无B无 |
doCreateBean(A,mdb,args) | A有B无 | A无B无 | A无B无 |
populateBean(A, mbd, instanceWrapper) | 填充属性A ,同时由A解析B…… | ||
getSingleton(B, true) | A有B无 | A无B无 | A无B无 |
doCreateBean(B,mdb,args) | A有B有 | A无B无 | A无B无 |
populateBean(B, mbd, instanceWrapper) | 填充属性B,由B准备解析A…… | ||
getSingleton(A, true) | A无B有 | A有B无 | A无B无 |
完成populateBean(B, mbd, instanceWrapper)解析…… | |||
addSingleton(B, singletonObject) | A无B无 | A有B无 | A无B有 |
完成populateBean(A, mbd, instanceWrapper) | |||
A- = initializeBean(beanName, exposedObject, mbd)在initializeBean之后A变为A- | |||
getSingleton(A, false);验证 | addSingleton(A, singletonObject) …… |
在上面这个过程中,在对A进行验证时,就会从earlySingletonObjects中取得一个A,但是这个A和后面的A-可能不是同一个对象,这是因为有了beanPostProcessor存在,它可以改变bean的最终值,比如对原始bean进行封装,代理等。在这个过程中,出现了3个对象A,A-,B,而B中所持有的A对象为原始的A。如果这里的A和A-不是同一个对象,即产生了beanA有了beanB的引用,但beanB并没有beanA的引用,而是另一个beanA的引用。这肯定不满足条件。
那么我们看Spring 对这种情况的处理:
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {//判断点1
if (exposedObject == bean) {//判断点2
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {判断点3
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {判断点4
抛出对象不致异常。
}
判断点1,首先确定这个对象能从earlySingletonObjects中取出对象来,经过上面的分析,我们知道,在正常情况下,此对象为null,即不存在循环检测。而在循环引用中,此对象能够被取出来。
判断点2,再判断这个对象和当前通过beanPostProcessor处理过的对象是否相同,如果相同,表示对象没有经过修改,即A=A-,那么循环引用成立。无需处理
判断点3,判断当前对象A是否被其他对象所依赖,在循环引用中,已经处理了A和B,那么在依赖表中,即在属性dependentBeanMap和dependenciesForBeanMap中。其中A->B表示A依赖于B,B->A表示B依赖于A。那么在dependentBeanMap中就会出现两个entry,分别为A->B和B->A。这里A依赖于A,那么表示A已经被依赖,则进入进一步检测中。在检测中,将取得一个A的被依赖列表中的bean已经被创建的对象列表值。
判断点4,如果被依赖对象列表不为空,则表示出现循环引用。因为按照创建规则,如果A->B,则必须先创建B,而B->A,则必须先创建A。在这里,A被B依赖,就要求A必须在B之前被创建,而B又被A依赖,又要求A必须在B之前被创建。这创建的两个对象必须满足一致才可以。即在A->B中的两个对象,必须和B->A的两个对象,互相一致才可以,否则就不是循环引用。
至此,整个流程梳理清楚。那么,如何处理这种循环引用呢?答案其实也很简单,在xml中将两方的循环切掉。然后使用一个beanPostProcessor即可以,此beanPostProcessor必须要在放到所有beanPostPrcessor的最后面。然后此beanPostProcessor,这样写即可:
判断当前bean为beanA
BeanB beanB=beanFactory.getBean(“beanB”);
beanA.setBeanB(beanB);
beanB.setBeanA(beanA);
想具体了解的 可以看看一下博客
Spring 循环依赖详细介绍