Spring 系列目錄(http://www.javashuo.com/article/p-kqecupyl-bm.html)html
Spring 循環引用相關文章:java
Spring 對單例 bean 的管理都是在 DefaultSingletonBeanRegistry 中完成的,這裏會涉及到其內部所使用的幾個內部屬性:spring
// 1.1 保存最終建立成功的單例 beanName -> beanInstance private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 1.2 中間變量,beanName -> Objectfactory private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 1.3 中間變量,bean 還在建立的時候就能夠獲取,用於檢測循環引用 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
這裏涉及用於存儲 bean 的不一樣的 Map,可能讓人感到崩潰,簡單解釋以下:緩存
singletonObjects
:用於保存 beanName 和建立 bean 實例之間的關係。beanName -> beanInstancesingletonFactories
:用於保存 beanName 和對象工廠的引用關係,一旦最終對象被建立(經過 objectFactory.getObject()),此引用信息將刪除。beanName -> ObjectfactoryearlySingletonObjects
:用於保存 beanName 和建立的原始 bean 的引用關係,注意這裏是原始 bean,即便用工廠方法或構造方法建立出來的對象,一旦對象最終建立好,此引用信息將刪除。 與 singletonObjects 的不一樣之處在於,此時 bean 還在建立過程當中,並且以後還能夠進行加強,也就是代理後這兩個 bean 就不是同一個了。能夠經過 getBean 方法獲取到了,其目的是用來檢測循環引用。從上面的解釋,能夠看出,這 singletonFactories 和 earlySingletonObjects 都是一個臨時的輔助狀態。在全部的對象建立完畢以後,此兩個對象的 size 都爲 0。那麼再來看下這兩個對象如何進行協做:app
(1) 方法1:建立單例對象ide
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // 1. 將這個 bean 添加到 singletonsCurrentlyInCreation 集合中,這樣就能夠判斷 bean 是否存在建立 beforeSingletonCreation(beanName); // 2. 初始化 bean,委託給 ObjectFactory 完成 singletonObject = singletonFactory.getObject(); // 3. 從 singletonsCurrentlyInCreation 移除該 bean afterSingletonCreation(beanName); // 4. 建立完成進行註冊,這樣下次就能夠從緩存中直接獲取這個對象了 addSingleton(beanName, singletonObject); } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
(2) 方法2:單例對象建立完成進行註冊源碼分析
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
(3) 方法3:將實例化後的對象暴露到容器中this
Spring 在 bean 實例化後就會調用 addSingletonFactory 將這個對象提早暴露到容器中,這們就能夠經過 getBean(A) 獲得這個對象,即便這個對象仍正在建立。用於解決循環依賴。代理
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
(4) 方法4:從緩存中獲取 beancode
這個方法也是專門用於解決循環依賴的問題,當不存在循環依賴時 earlySingletonObjects 老是 null。
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
咱們從 BeanFactory#getBean(beanName) 調用提及,看一下這幾個方法的調用順序:
這個方法先從緩存中獲取 bean,沒有再建立 bean,所以會調用方法 4 和方法 1,咱們看一下調用過程。
(1) getSingleton(beanName, true)
doGetBean 首先從緩存中獲取數據,Object sharedInstance = getSingleton(beanName)
,這個方法最終會調用 getSingleton(beanName, true)
。
(2) getSingleton(beanName, singletonFactory)
若是緩存中沒有 bean,則會調用 addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
來建立一個新 bean,代碼以下:
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
一旦調用 getSingleton(beanName, singletonFactory) 方法,這個方法建立開始時就會標記這個 bean 爲正在建立,建立結束後移除對應的標記。直接建立 bean 的過程其實是委託給了 createBean 方法。繼續跟蹤這個方法。
doCreateBean 方法中完成了單例的 bean 有如下幾個主要的步驟:
createBeanInstance
實例化 bean 對象,通常是經過反射調用默認的構造器。populateBean
bean 屬性注入,在這個步驟會從 Spring 容器中查找對應屬性字段的值,解決循環依賴問題。initializeBean
調用的 bean 定義的初始化方法。(3) addSingletonFactory(beanName, singletonFactory)
在 createBeanInstance 後 populateBean 前 Spring 會將這個實例化的 bean 提早暴露到容器中,這樣 populateBean 屬性注入時就能夠經過 getBean(A) 查找到。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
(4) getSingleton(beanName, false)
在 bean 初始化完成還後還須要進行依賴的檢查,這時由於提早暴露的這個 bean(即便用工廠方法或構造方法建立出來的對象) initializeBean 還能夠進行加強,這樣這兩個 bean 就不是同一個了。Spring 默認是不容許這種狀況發生的。
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } }
(5) addSingleton(beanName, singletonObject)
在 bean 建立結束後還有一步是在 getSingleton(beanName, singletonFactory) 中完成的,調用 addSingleton(beanName, singletonObject),即註冊最終的 bean,同時清空中間的輔助狀態。
這樣單例 bean 的建立過程就完成了,下面就須要分析循環引用下 singletonFactories、earlySingletonObjects 這兩個集合的狀態。
在正常的狀況下,調用順序以下:如下有無,表示是否持有對指定 Bean 的引用
過程 | 方法 | singletonFactories | earlySingletonObjects | singletonObjects |
---|---|---|---|---|
緩存中獲取 | getSingleton(beanName, true) | 無 | 無 | 無 |
建立 bean | getSingleton(beanName, singletonFactory) | 無 | 無 | 無 |
提早暴露到容器中 | addSingletonFactory(beanName, singletonFactory) | 有 | 無 | 無 |
依賴檢查 | getSingleton(beanName, false) | 有 | 無 | 無 |
註冊 | addSingleton(beanName, singletonObject) | 無 | 無 | 有 |
能夠看到正常狀況下,單例 bean 暴露的對象只會出如今 singletonFactories 集合中,不可能出如今 earlySingletonObjects 集合中,除非在建立 bean 的過程當中又調用了 getSingleton(beanName, true) 方法,也就是此時出現了循環引用。
可是出現循環引用以後呢,就會出現這種狀況:
過程 | 方法 | singletonFactories | earlySingletonObjects | singletonObjects |
---|---|---|---|---|
緩存中獲取 A | getSingleton(A, true) | A無B無 | A無B無 | A無B無 |
建立 A | getSingleton(A, singletonFactory) | A無B無 | A無B無 | A無B無 |
暴露 A 到容器中 | addSingletonFactory(A, singletonFactory) | A有B無 | A無B無 | A無B無 |
populateBean(A, mbd, instanceWrapper) A 注入 B 時又依賴了 A,此時由 B 準備解析 A…… | ||||
緩存中獲取 A | getSingleton(A, true) | A無B有 | A有B無 | A無B無 |
註冊 B | addSingleton(B, singletonObject) | A無B無 | A有B無 | A無B有 |
populateBean(A, mbd, instanceWrapper) 完成屬性注入 | ||||
A- = initializeBean(beanName, exposedObject, mbd) 在 initializeBean 以後 A 變爲 A- | ||||
依賴檢查 | getSingleton(beanName, false) | A無B無 | A有B無 | A無B有 |
註冊 A | getSingleton(beanName, false) | A無B無 | A無B無 | A有B有 |
在上面這個過程當中,在對 A 進行驗證時,就會從 earlySingletonObjects 中取得一個 A,可是這個 A 和後面的 A- 可能不是同一個對象,這是由於有了 beanPostProcessor 存在,它能夠改變 bean 的最終值,好比對原始 bean 進行封裝,代理等。在這個過程當中,出現了 3 個對象 A, A-, B,而 B 中所持有的 A 對象爲原始的 A。若是這裏的 A 和 A- 不是同一個對象,即產生了 beanA 有了 beanB 的引用,但 beanB 並無 beanA 的引用,而是另外一個 beanA 的引用。
天天用心記錄一點點。內容也許不重要,但習慣很重要!