Spring源碼-IOC容器(六)-bean的循環依賴

Spring IOC容器 源碼解析系列,建議你們按順序閱讀,歡迎討論java

(spring源碼均爲4.1.6.RELEASE版本)spring

  1. Spring源碼-IOC容器(一)-構建簡單IOC容器
  2. Spring源碼-IOC容器(二)-Bean的定位解析註冊
  3. Spring源碼-IOC容器(三)-GetBean
  4. Spring源碼-IOC容器(四)-FactoryBean
  5. Spring源碼-IOC容器(五)-Bean的初始化
  6. Spring源碼-IOC容器(六)-bean的循環依賴
  7. Spring源碼-IOC容器(七)-ApplicationContext
  8. Spring源碼-IOC容器(八)-NamespaceHandler與自定義xml
  9. Spring源碼-IOC容器(九)-Component-Scan源碼解析
  10. Spring源碼-IOC容器(十)-@Autowired解析

不知道你們有沒有想過這樣一種狀況,在Spring的配置中,存在兩個bean A和bean B,A依賴於B,B依賴於A,即A和B相互依賴(引用),xml配置以下:緩存

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	
	<bean id="beanA" class="com.lntea.spring.demo.bean.BeanA">
		<property name="beanB" ref="beanB"></property>
	</bean>
	
	<bean id="beanB" class="com.lntea.spring.demo.bean.BeanB">
		<property name="beanA" ref="beanA"></property>
	</bean>
	
</beans>

BeanA.java併發

package com.lntea.spring.demo.bean;

public class BeanA {

	private BeanB beanB;
	
	public void print(){
		System.out.println("beanB:" + beanB + " beanA:" + beanB.getBeanA());
	}

	public BeanB getBeanB() {
		return beanB;
	}

	public void setBeanB(BeanB beanB) {
		this.beanB = beanB;
	}
	
	
}

BeanB.javaide

package com.lntea.spring.demo.bean;

public class BeanB {

	private BeanA beanA;

	public BeanA getBeanA() {
		return beanA;
	}

	public void setBeanA(BeanA beanA) {
		this.beanA = beanA;
	}
	
	
}

此時經過BeanFactory獲取beanA,並調用print方法this

BeanA beanA = beanFactory.getBean("beanA",BeanA.class);
beanA.print();

輸出結果爲spa

beanB:com.lntea.spring.demo.bean.BeanB@59a6e353 beanA:com.lntea.spring.demo.bean.BeanA@7a0ac6e3

能夠看出beanA拿到了beanB的引用,beanB同時也拿到了beanA的引用。可見在Spring中是支持循環引用的,怎麼實現的,有沒有限制,咱們再從源碼來解析一下。.net

getBean方法首先會從緩存中查詢是否存在建立好的單例debug

Object sharedInstance = getSingleton(beanName);

public Object getSingleton(String beanName) {
	return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 查詢緩存中是否有建立好的單例
	Object singletonObject = this.singletonObjects.get(beanName);
	// 若是緩存不存在,判斷是否正在建立中
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		// 加鎖防止併發
		synchronized (this.singletonObjects) {
			// 從earlySingletonObjects中查詢是否有early緩存
			singletonObject = this.earlySingletonObjects.get(beanName);
			// early緩存也不存在,且容許early引用
			if (singletonObject == null && allowEarlyReference) {
				// 從單例工廠Map裏查詢beanName
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					// singletonFactory存在,則調用getObject方法拿到單例對象
					singletonObject = singletonFactory.getObject();
					// 將單例對象添加到early緩存中
					this.earlySingletonObjects.put(beanName, singletonObject);
					// 移除單例工廠中對應的singletonFactory
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

從以上的代碼能夠看出code

  1. 只針對單例的bean,多例的後面討論
  2. 默認的singletonObjects緩存不存在要get的beanName時,判斷beanName是否正在建立中
  3. 從early緩存earlySingletonObjects中再查詢,early緩存是用來緩存已實例化但未組裝完成的bean
  4. 若是early緩存也不存在,從singletonFactories中查找是否有beanName對應的ObjectFactory對象工廠
  5. 若是對象工廠存在,則調用getObject方法拿到bean對象
  6. 將bean對象加入early緩存,並移除singletonFactories的對象工廠

上面最重要的就是singletonFactories什麼時候放入了能夠經過getObject得到bean對象的ObjectFactory。根據咱們的猜想,應該會是bean對象實例化後,而屬性注入以前。仔細尋找後發現,在AbstractAutowireCapableBeanFactory類的doCreateBean方法,也就是實際bean建立的方法中,執行完createBeanInstance實例化bean以後有一段代碼:

// bean爲單例且容許循環引用且正在建立中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
		isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
	if (logger.isDebugEnabled()) {
		logger.debug("Eagerly caching bean '" + beanName +
				"' to allow for resolving potential circular references");
	}
	// 建立ObjectFactory並添加到singletonFactories中
	addSingletonFactory(beanName, new ObjectFactory<Object>() {
		[@Override](https://my.oschina.net/u/1162528)
		public Object getObject() throws BeansException {
			return getEarlyBeanReference(beanName, mbd, bean);
		}
	});
}


protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
		// 判斷默認緩存中沒有beanName
		if (!this.singletonObjects.containsKey(beanName)) {
			// 添加ObjectFactory到singletonFactories
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

當判斷bean爲單例且正在建立中,而Spring容許循環引用時,將能得到bean對象的引用的ObjectFactory添加到singletonFactories中,此時就與以前的getSingleton方法相呼應。而allowCircularReferences標識在spring中默認爲true,可是也能夠經過setAllowCircularReferences方法對AbstractAutowireCapableBeanFactory進行設置。

再來看下getObject方法中的getEarlyBeanReference方法。這裏也設置了一個InstantiationAwareBeanPostProcessor後置處理器的擴展點,容許在對象返回以前修改甚至替換bean。

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				if (exposedObject == null) {
					return exposedObject;
				}
			}
		}
	}
	return exposedObject;
}

來梳理一下上面getBean("beanA")的執行過程

  1. 實例化BeanA
  2. 將能獲取BeanA對象的ObjectFactory添加到singletonFactories中
  3. BeanA注入BeanB屬性,調用getBean("beanB")方法
  4. 實例化BeanB
  5. 將能獲取BeanB對象的ObjectFactory添加到singletonFactories中
  6. BeanB注入BeanA屬性,調用getBean("beanA")
  7. 從singletonFactories中獲取ObjectFactory並調用getObject方法拿到beanA對象的引用
  8. BeanB建立完成,注入到BeanA的beanB屬性中
  9. BeanA建立完成返回

上面咱們瞭解了單例的bean循環引用的處理過程,那麼多例的呢?其實咱們能夠按上面的思路來思考一下,單例bean的循環引用是由於每一個對象都是固定的,只是提早暴露對象的引用,最終這個引用對應的對象是建立完成的。可是多例的狀況下,每次getBean都會建立一個新的對象,那麼應該引用哪個對象呢,這自己就已是矛盾的了。於是spring中對於多例之間相互引用是會提示錯誤的。

// 若是已經存在多例的對象在建立中,就會拋出異常
if (isPrototypeCurrentlyInCreation(beanName)) {
	throw new BeanCurrentlyInCreationException(beanName);
}

Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

可見spring會認爲多例之間的循環引用是沒法解決的。

相關文章
相關標籤/搜索