[转]spring 学习-bean创建-循环依赖-3
Posted float123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]spring 学习-bean创建-循环依赖-3相关的知识,希望对你有一定的参考价值。
接上文中对调用点A和调用点B的调用方法进行了陈述。接下来,针对创建bean的不同顺序对调用点和调用方法进行分析。
在正常的情况下,调用顺序如下:以下有无,表示是否持有对指定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) 解析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准备解析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对这种情况的处理,即在上文中的方法3,再次将代码贴在下面:
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
抛出对象不致异常。
}
上面有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 学习-bean创建-循环依赖-3的主要内容,如果未能解决你的问题,请参考以下文章