Spring IOC容器 源碼解析系列,建議你們按順序閱讀,歡迎討論java
(spring源碼均爲4.1.6.RELEASE版本)spring
不知道你們有沒有想過這樣一種狀況,在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
上面最重要的就是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")
的執行過程
上面咱們瞭解了單例的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會認爲多例之間的循環引用是沒法解決的。