Spring中循环引用的处理-3

2012/08/28 10:21:36 2 Comments

上文中对调用点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中循环引用的处理-1
Spring中循环引用的处理-2

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201208280003.html

相关文章:

已有2 个评论

  1. shantao Says:

    你好,上面有4个判断点还是没太明白

  2. hackem Says:

    写错了吧?
    doCreateBean(beanName,mdb,args)下面一行的getSingleton(beanName, true); 应为
    getSingleton(beanName, false);
    不知道我说的对不对。。。

留下足迹