1. 程式人生 > >spring迴圈依賴

spring迴圈依賴

迴圈依賴解決

constructor(此種無法解決)

(看完sertter之後再來看這部分吧)
我想看過下部分setter解決迴圈依賴的實現之後,肯定有想法為啥constructor無法實現,我們同樣可以提前曝光一個SingletonFactory(以下簡稱fac)噻?
其實不然,當然,我們想法是沒有問題的,我們當然可以提前暴露一個fac,但是為什麼此種方式不行。
首先我們分析一下為什麼setter能夠實現,我們知道在setter中是在bean例項化完畢之後,
這裡很重要,因為是例項化之後,所有這個時候A其實是已經存在了,只是還沒有填充屬性而已,
在populateBean填充屬性的時候發現沒有B,然後再去建立B, 這個時候B可以通過提前曝光的fac拿到的A例項,
看清楚了,是拿到的A的例項,不是fac。這種情況A在填充完屬性之後,B持有的A地址不會變,
所以這種情況提前曝光可以解決迴圈依賴。
再來看constructor的情況,我們說了,我們當然可以天暴露一個fac(可以在建構函式執行前暴露),
但是當我們在初始化B,為B填充屬性A的時候,你覺得我們能從fac中拿到一個A的地址嗎?當然不能咯,
因為A的constructor並未執行完成,這種情況下,A是沒有分配到地址的,也就是說A並不存在,而我們只有一個fac,
這個fac的getObject是沒辦法獲取到A的,因為A還不存在啊,但是B需要的是一個A型別的屬性,而不是一個Factory,
所以無法注入!無法注入怎麼辦,無法注入當然就無法實現咯!

setter

提前暴露一個單例bean的工廠方法,使其他bean能引用到該bean。

circle_process

//AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
	...
		//提前暴露條件,當前例項是單例 && 允許迴圈依賴 && 當前例項正在建立中
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			...
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				public Object getObject() throws BeansException {
                    //對bean再一次依賴引用,主要應用SmartInstantiationAware BeanPostProcessor,
                    //AOP就是在這裡將advice動態之織入bean中,若沒有則直接返回bean,不作任何處理
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}
    ...
	}

code

 <bean id="circleA" class="com.wt.test.circle.CircleA">
     <property name="circleB" ref="circleB"/>
 </bean>
 <bean id="circleB" class="com.wt.test.circle.CircleB">
     <property name="circleA" ref="circleA"/>
 </bean>
private static void circleSolution() {
		ClassPathXmlApplicationContext x = new ClassPathXmlApplicationContext("application-circle.xml");
		CircleA a = x.getBean(CircleA.class);
		CircleB b = x.getBean(CircleB.class);
		System.out.println(a);
		System.out.println(b);
		System.out.println(a.getCircleB() == b);
		System.out.println(b.getCircleA() == a);
	}

結果

circle_1

prototype模式

xml

<bean id="circleA" class="com.wt.test.circle.CircleA" scope="prototype">
    <property name="circleB" ref="circleB"/>
</bean>
<bean id="circleB" class="com.wt.test.circle.CircleB" scope="prototype">
    <property name="circleA" ref="circleA"/>
</bean>

結果

circle_2

注意:只有在單例模式下才能解決迴圈依賴,prototype作用域的bean是無法完成迴圈依賴注入的,因為Spring容器不進行快取prototype作用域的bean,因此無法提前暴露出一個建立中的bean!