1. 程式人生 > >死磕Spring之IoC篇 - 開啟 Bean 的載入

死磕Spring之IoC篇 - 開啟 Bean 的載入

> 該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 [Spring 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring 版本:5.1.14.RELEASE > > 開始閱讀這一系列文章之前,建議先檢視[**《深入瞭解 Spring IoC(面試題)》**](https://www.cnblogs.com/lifullmoon/p/14422101.html)這一篇文章 > > 該系列其他文章請檢視:[**《死磕 Spring 之 IoC 篇 - 文章導讀》**](https://www.cnblogs.com/lifullmoon/p/14436372.html) ## 開啟 Bean 的載入 前面的一些列文章對**面向資源(XML、Properties)**、**面向註解**定義的 Bean 是如何被解析成 BeanDefinition(Bean 的“前身”),並儲存至 BeanDefinitionRegistry 註冊中心裡面,實際也是通過 ConcurrentHashMap 進行儲存。 Spring 底層 IoC 容器 DefaultListableBeanFactory,實現了 BeanFactory 和 BeanDefinitionRegistry 介面,這個時候它處於“就緒狀態”,當我們顯示或者隱式地呼叫 `getBean(...)` 方法時,會觸發載入 Bean 階段,獲取對應的 Bean。在該方法中,如果是單例模式會先從快取中獲取,已有則直接返回,沒有則根據 BeanDefinition 開始初始化這個 Bean。 ### BeanFactory 體系結構 先來看看 BeanFactory 介面的繼承關係
簡單描述這些介面: - `org.springframework.beans.factory.BeanFactory`,Spring IoC 容器最基礎的介面,提供依賴查詢**單個** Bean 的功能 - `org.springframework.beans.factory.ListableBeanFactory`,繼承 BeanFactory 介面,提供依賴查詢**多個** Bean 的功能 - `org.springframework.beans.factory.HierarchicalBeanFactory`,繼承 BeanFactory 介面,提供獲取父 BeanFactory 的功能,具有**層次性** - `org.springframework.beans.factory.config.ConfigurableBeanFactory`,繼承 HierarchicalBeanFactory 介面,提供可操作內部相關元件的功能,具有**可配置性** - `org.springframework.beans.factory.config.AutowireCapableBeanFactory`,繼承 BeanFactory 介面,提供可注入的功能,支援**依賴注入** - `org.springframework.beans.factory.config.ConfigurableListableBeanFactory`,繼承上面所有介面,綜合所有特性,還提供可提前初始化所有單例 Bean 的功能 通過這些介面的名稱可以大致瞭解其用意,接下來我們來看看它們的實現類的繼承關係
簡單描述這些實現類: - `org.springframework.beans.factory.support.AbstractBeanFactory` 抽象類,實現 ConfigurableBeanFactory 介面,基礎實現類,Bean 的建立過程交由子類實現 - `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory` 抽象類,繼承 AbstractBeanFactory,實現 AutowireCapableBeanFactory 介面,完成 Bean 的建立 - `org.springframework.beans.factory.support.DefaultListableBeanFactory`,Spring 底層 IoC 容器,依賴注入的底層實現 其他的介面和類和 BeanDefinition 註冊中心,別名註冊中心,單例 Bean 註冊中心相關;右下角的 ApplicationContext 與 Spring 應用上下文有關,它的整個體系這裡不做展述,在後面的文章進行分析 ### AbstractBeanFactory `org.springframework.beans.factory.support.AbstractBeanFactory` 抽象類,實現 ConfigurableBeanFactory 介面,BeanFactory 的基礎實現類,提供依賴查詢方法,可獲取 Bean 物件,接下來我們來看看依賴查詢的實現 #### getBean 方法 `getBean(String name)` 方法,根據名稱獲取 Bean,當然還有許多過載方法,如下: ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } @Override public T getBean(String name, Class requiredType) throws BeansException { return doGetBean(name, requiredType, null, false); } @Override public Object getBean(String name, Object... args) throws BeansException { return doGetBean(name, null, args, false); } public T getBean(String name, @Nullable Class requiredType, @Nullable Object... args) throws BeansException { return doGetBean(name, requiredType, args, false); } ``` 最終都會呼叫 `doGetBean(...)` 這個方法 > 當我們顯示或者隱式地呼叫這個方法時,會觸發 Bean 的載入;你是否會有疑問,我們使用 Spring 的過程中並不會呼叫這個方法去獲取 Bean,那這個方法會被誰呼叫呢?在 ConfigurableListableBeanFactory 介面中提供提前初始化所有單例 Bean 的功能,在 Spring 應用上下文(ApplicationContext)重新整理階段會提前初始化所有的單例 Bean,這個提前初始化也是呼叫 getBean 這個方法,這部分內容在後續分析 Spring 應用上下文的生命週期會講到 #### 【核心】doGetBean 方法 `doGetBean(final String name, @Nullable final Class requiredType, @Nullable final Object[] args, boolean typeCheckOnly)` 方法,獲取一個 Bean,方法如下: ```java protected T doGetBean(final String name, @Nullable final Class requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // <1> 獲取 `beanName` // 因為入參 `name` 可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(`&` 開頭,需要去除) // 所以需要獲取真實的 beanName final String beanName = transformedBeanName(name); Object bean; // <2> 先從快取(僅快取單例 Bean )中獲取 Bean 物件,這裡快取指的是 `3` 個 Map // 快取中也可能是正在初始化的 Bean,可以避免**迴圈依賴注入**引起的問題 // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); // <3> 若從快取中獲取到對應的 Bean,且 `args` 引數為空 if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } // <3.1> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回 // 否則,呼叫 FactoryBean#getObject() 獲取目標物件 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } // 快取中沒有對應的 Bean,則開啟 Bean 的載入 else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. // <4> 如果**非單例模式**下的 Bean 正在建立,這裡又開始建立,表明存在迴圈依賴,則直接丟擲異常 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); // <5> 如果從當前容器中沒有找到對應的 BeanDefinition,則從父容器中載入(如果存在父容器) if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. // <5.1> 獲取 `beanName`,因為可能是別名,則進行處理 // 和第 `1` 步不同,不需要對 `&` 進行處理,因為進入父容器重新依賴查詢 String nameToLookup = originalBeanName(name); // <5.2> 若為 AbstractBeanFactory 型別,委託父容器的 doGetBean 方法進行處理 // 否則,就是非 Spring IoC 容器,根據引數呼叫相應的 `getBean(...)`方法 if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } // <6> 如果不是僅僅做型別檢查,則表示需要建立 Bean,將 `beanName` 標記為已建立過 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // <7> 從容器中獲取 `beanName` 對應的的 RootBeanDefinition(合併後) final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // 檢查是否為抽象類 checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. // <8> 獲取當前正在建立的 Bean 所依賴物件集合(`depends-on` 配置的依賴) String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // <8.1> 檢測是否存在迴圈依賴,存在則丟擲異常 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // <8.2> 將 `beanName` 與 `dep` 之間依賴的關係進行快取 registerDependentBean(dep, beanName); try { // <8.3> 先建立好依賴的 Bean(重新呼叫 `getBean(...)` 方法) getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance. // <9> 開始建立 Bean,不同模式建立方式不同 if (mbd.isSingleton()) { // <9.1> 單例模式 /* * <9.1.1> 建立 Bean,成功建立則進行快取,並移除快取的早期物件 * 建立過程實際呼叫的下面這個 `createBean(...)` 方法 */ sharedInstance = getSingleton(beanName, // ObjectFactory 實現類 () -> { try { // **【核心】** 建立 Bean return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. // 如果建立過程出現異常,則顯式地從快取中刪除當前 Bean 相關資訊 // 在單例模式下為了解決迴圈依賴,建立過程會快取早期物件,這裡需要進行刪除 destroySingleton(beanName); throw ex; } }); // <9.1.2> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回 // 否則,呼叫 FactoryBean#getObject() 獲取目標物件 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // <9.2> 原型模式 else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { // <9.2.1> 將 `beanName` 標記為原型模式正在建立 beforePrototypeCreation(beanName); // <9.2.2> **【核心】** 建立 Bean prototypeInstance = createBean(beanName, mbd, args); } finally { // <9.2.3> 將 `beanName` 標記為不在建立中,照應第 `9.2.1` 步 afterPrototypeCreation(beanName); } // <9.2.4> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回 // 否則,呼叫 FactoryBean#getObject() 獲取目標物件 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } // <9.3> 其他模式 else { // <9.3.1> 獲取該模式的 Scope 物件 `scope`,不存在則丟擲異常 String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { // <9.3.1> 從 `scope` 中獲取 `beanName` 對應的物件(看你的具體實現),不存在則執行**原型模式**的四個步驟進行建立 Object scopedInstance = scope.get(beanName, () -> { // 將 `beanName` 標記為原型模式正在建立 beforePrototypeCreation(beanName); try { // **【核心】** 建立 Bean return createBean(beanName, mbd, args); } finally { // 將 `beanName` 標記為不在建立中,照應上一步 afterPrototypeCreation(beanName); } }); // 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回 // 否則,呼叫 FactoryBean#getObject() 獲取目標物件 bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. // <10> 如果入參 `requiredType` 不為空,並且 Bean 不是該型別,則需要進行型別轉換 if (requiredType != null && !requiredType.isInstance(bean)) { try { // <10.1> 通過型別轉換機制,將 Bean 轉換成 `requiredType` 型別 T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); // <10.2> 轉換後的 Bean 為空則丟擲異常 if (convertedBean == null) { // 轉換失敗,丟擲 BeanNotOfRequiredTypeException 異常 throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } // <10.3> 返回型別轉換後的 Bean 物件 return convertedBean; } catch (TypeMismatchException ex) { if (logger.isTraceEnabled()) { logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } // <11> 返回獲取到的 Bean return (T) bean; } ``` 這個方法的處理過程有點長,如下: 1. 獲取 `beanName`,因為入參 `name` 可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(`&` 開頭,需要去除),所以需要獲取真實的 `beanName` 2. 先從快取(僅快取單例 Bean )中獲取 Bean 物件,這裡快取指的是 `3` 個 Map;快取中也可能是正在初始化的 Bean,可以避免**迴圈依賴注入**引起的問題 3. 若從快取中獲取到對應的 Bean,且 `args` 引數為空 1. **【同】**呼叫 `getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd)` 方法 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回,否則,呼叫 FactoryBean#getObject() 獲取目標物件 ------ 快取中沒有對應的 Bean,則開啟 Bean 的載入 4. 如果**非單例模式**下的 Bean 正在建立,這裡又開始建立,表明存在迴圈依賴,則直接丟擲異常 5. 如果從當前容器中沒有找到對應的 BeanDefinition,則從**父容器**中載入(如果存在父容器) 1. 獲取 `beanName`,因為可能是別名,則進行處理,和第 `1` 步不同,不需要對 `&` 進行處理,因為進入父容器重新依賴查詢 2. 若為 AbstractBeanFactory 型別,委託父容器的 doGetBean 方法進行處理;否則,就是非 Spring IoC 容器,根據引數呼叫相應的 `getBean(...)`方法 6. 如果不是僅僅做型別檢查,則表示需要建立 Bean,將 `beanName` 標記為已建立過,在後面的**迴圈依賴檢查**中會使用到 7. 從容器中獲取 `beanName` 對應的的 RootBeanDefinition(合併後),呼叫 `getMergedLocalBeanDefinition(String beanName)` 方法 8. 獲取當前正在建立的 Bean 所依賴物件集合(`depends-on` 配置的依賴) 1. 檢測是否存在迴圈依賴,存在則丟擲異常 2. 將 `beanName` 與 `dep` 之間依賴的關係進行快取 3. 先建立好依賴的 Bean(重新呼叫 `getBean(...)` 方法) ------ 9. 開始建立 Bean,不同模式建立方式不同 1. **單例模式** 1. 建立 Bean,成功建立則進行快取,並移除快取的早期物件,呼叫 `getSingleton(String beanName, ObjectFactory singletonFactory)` 方法 **【核心】**入參的 ObjectFactory 實現類就是呼叫的 `AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])` 方法 2. **【同】** 和上面的 `3.1` 相同操作 2. **原型模式** 1. 將 `beanName` 標記為**非單例模式**正在建立 2. **【核心】** 建立 Bean,呼叫 `AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])` 方法 3. 將 `beanName` 標記為不在建立中,照應第 `9.2.1` 步 4. **【同】** 和上面的 `3.1` 相同操作 3. **其他模式** 1. 獲取該模式的 Scope 物件 `scope`,不存在則丟擲異常 2. 從 `scope` 中獲取 `beanName` 對應的物件(看你的具體實現),不存在則執行**原型模式**的四個步驟進行建立 ------ 10. 如果入參 `requiredType` 不為空,並且 Bean 不是該型別,則需要進行型別轉換 1. 通過型別轉換機制,將 Bean 轉換成 `requiredType` 型別 2. 轉換後的 Bean 為空則丟擲異常 3. 返回型別轉換後的 Bean 物件 11. 返回獲取到的 Bean ------ 概括: - 可以看到這個方法載入 Bean 的過程中,會先從快取中獲取**單例模式**的 Bean; - 不管是從快取中獲取的還是新建立的,都會進行處理,如果是 FactoryBean 型別則呼叫其 getObject() 獲取目標物件; - BeanFactory 可能有父容器,如果當前容器找不到 BeanDefinition 則會嘗試讓父容器建立; - 建立 Bean 的任務交由 AbstractAutowireCapableBeanFactory 去完成; - 如果獲取到的 Bean 不是我們想要型別,會通過型別轉換機制轉換成目標型別 接下來依次分析上述過程的相關步驟(`doGetBean(...)`) ### 1. 獲取 beanName 對應程式碼段: ```java // AbstractBeanFactory.java final String beanName = transformedBeanName(name); ``` 因為入參 `name` 可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(`&` 開頭,需要去除),所以需要進行一番轉換,如下: ```java // AbstractBeanFactory.java protected String transformedBeanName(String name) { return canonicalName(BeanFactoryUtils.transformedBeanName(name)); } // BeanFactoryUtils.java public static String transformedBeanName(String name) { Assert.notNull(name, "'name' must not be null"); if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { return name; } // 獲取 name 對應的 beanName, // 不為 null 則返回 `transformedBeanNameCache` 快取中對應的 beanName, // 為 null 則對 name 進行處理,將字首 '&' 去除,直至沒有 '&',然後放入 `transformedBeanNameCache` 快取中,並返回處理後的 beanName return transformedBeanNameCache.computeIfAbsent(name, beanName -> { do { beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); } while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); return beanName; }); } // SimpleAliasRegistry.java public String canonicalName(String name) { String canonicalName = name; // Handle aliasing... String resolvedName; // 迴圈,從 aliasMap 中,獲取到最終的 beanName do { resolvedName = this.aliasMap.get(canonicalName); if (resolvedName != null) { canonicalName = resolvedName; } } while (resolvedName != null); return canonicalName; } ``` 過程並不複雜,先將字首 `&` 去除(如果存在),如果是別名則獲取對應的 `beanName` > 定義了一個 FactoryBean 型別的 Bean,名稱為 `user`,通過 `user` 獲取 Bean,獲取到的是 FactoryBean#getObject() 返回的物件(只會被呼叫一次) > > 通過 `&user` 獲取 Bean,獲取到的是 FactoryBean 本身這個物件 ### 2. 從快取中獲取單例 Bean 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 Object sharedInstance = getSingleton(beanName); ``` **單例模式**的 Bean 被建立後會快取,為了避免**迴圈依賴注入**,在建立過程會臨時快取正在建立的 Bean(早期 Bean),在後續文章會講到,從快取中獲取物件過程如下: ```java // DefaultSingletonBeanRegistry.java public Object getSingleton(String beanName) { return getSingleton(beanName, true); } protected Object getSingleton(String beanName, boolean allowEarlyReference) { // <1> **【一級 Map】**從單例快取 `singletonObjects` 中獲取 beanName 對應的 Bean Object singletonObject = this.singletonObjects.get(beanName); // <2> 如果**一級 Map**中不存在,且當前 beanName 正在建立 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // <2.1> 對 `singletonObjects` 加鎖 synchronized (this.singletonObjects) { // <2.2> **【二級 Map】**從 `earlySingletonObjects` 集合中獲取,裡面會儲存從 **三級 Map** 獲取到的正在初始化的 Bean singletonObject = this.earlySingletonObjects.get(beanName); // <2.3> 如果**二級 Map** 中不存在,且允許提前建立 if (singletonObject == null && allowEarlyReference) { // <2.3.1> **【三級 Map】**從 `singletonFactories` 中獲取對應的 ObjectFactory 實現類 ObjectFactory singletonFactory = this.singletonFactories.get(beanName); // 如果從**三級 Map** 中存在對應的物件,則進行下面的處理 if (singletonFactory != null) { // <2.3.2> 呼叫 ObjectFactory#getOject() 方法,獲取目標 Bean 物件(早期半成品) singletonObject = singletonFactory.getObject(); // <2.3.3> 將目標物件放入**二級 Map** this.earlySingletonObjects.put(beanName, singletonObject); // <2.3.4> 從**三級 Map**移除 `beanName` this.singletonFactories.remove(beanName); } } } } // <3> 返回從快取中獲取的物件 return singletonObject; } ``` 過程如下: 1. **【一級 Map】**從單例快取 `singletonObjects` 中獲取 beanName 對應的 Bean 2. 如果**一級 Map**中不存在,且當前 beanName 正在建立 1. 對 `singletonObjects` 加鎖 2. **【二級 Map】**從 `earlySingletonObjects` 集合中獲取,裡面會儲存從 **三級 Map** 獲取到的正在初始化的 Bean 3. 如果**二級 Map** 中不存在,且允許提前建立 1. **【三級 Map】**從 `singletonFactories` 中獲取對應的 ObjectFactory 實現類,如果從**三級 Map** 中存在對應的物件,則進行下面的處理 2. 呼叫 ObjectFactory#getOject() 方法,獲取目標 Bean 物件(早期半成品) 3. 將目標物件放入**二級 Map** 4. 從**三級 Map**移除 beanName 3. 返回從快取中獲取的物件 這個過程對應[**《深入瞭解 Spring IoC(面試題)》**](https://www.cnblogs.com/lifullmoon/p/14422101.html)中的**BeanFactory 是如何處理迴圈依賴**問題 ### 3. FactoryBean 的處理 > 一般情況下,Spring 通過反射機制利用 Bean 的 beanClass 屬性指定實現類來例項化 Bean。某些情況下,Bean 的例項化過程比較複雜,如果按照傳統的方式,則需要提供大量的配置資訊,配置方式的靈活性有限,這時採用編碼的方式可能會得到一個簡單的方案。Spring 為此提供了一個 FactoryBean 的工廠 Bean 介面,使用者可以通過實現該介面定製例項化 Bean 的邏輯。 > > FactoryBean 介面對於 Spring 框架本身也非常重要,其內部就提供了大量 FactoryBean 的實現。它們隱藏了例項化過程中一些複雜細節,給上層應用帶來了便利。 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); ``` 不管是從快取中獲取的還是新建立的,都會呼叫這個方法進行處理,如果是 FactoryBean 型別則呼叫其 getObject() 獲取目標物件 #### getObjectForBeanInstance 方法 ```java // AbstractBeanFactory.java protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory. // <1> 若 `name` 以 `&` 開頭,說明想要獲取 FactoryBean,則校驗其**正確性** if (BeanFactoryUtils.isFactoryDereference(name)) { // <1.1> 如果是 NullBean 空物件,則直接返回 if (beanInstance instanceof NullBean) { return beanInstance; } // <1.2> 如果不是 FactoryBean 型別,則丟擲異常 if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } } // Now we have the bean instance, which may be a normal bean or a FactoryBean. // If it's a FactoryBean, we use it to create a bean instance, unless the caller actually wants a reference to the factory. // 到這裡我們就有了一個 Bean,可能是一個正常的 Bean,也可能是一個 FactoryBean // 如果是 FactoryBean,則需要通過其 getObject() 方法獲取目標物件 // <2> 如果 `beanInstance` 不是 FactoryBean 型別,不需要再處理則直接返回 // 或者(表示是 FactoryBean 型別) `name` 以 `&` 開頭,表示你想要獲取實際 FactoryBean 物件,則直接返回 // 還不符合條件的話,表示是 FactoryBean,需要獲取 getObject() 返回目標物件 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } Object object = null; // <3> 如果入參沒有傳 BeanDefinition,則從 `factoryBeanObjectCache` 快取中獲取對應的 Bean 物件 // 入參傳了 BeanDefinition 表示這個 Bean 是剛建立的,不走快取,需要呼叫其 getObject() 方法獲取目標物件 // `factoryBeanObjectCache`:FactoryBean#getObject() 呼叫一次後返回的目標物件快取在這裡 if (mbd == null) { object = getCachedObjectForFactoryBean(beanName); } // <4> 若第 `3` 步獲取的物件為空,則需要呼叫 FactoryBean#getObject() 獲得物件 if (object == null) { // Return bean instance from factory. // <4.1> 將 `beanInstance` 轉換成 FactoryBean 型別 FactoryBean factory = (FactoryBean) beanInstance; // Caches object obtained from FactoryBean if it is a singleton. // <4.2> 如果入參沒有傳 BeanDefinition 並且當前容器存在對應的 BeanDefinition if (mbd == null && containsBeanDefinition(beanName)) { // 獲取對應的 RootBeanDefinition(合併後) mbd = getMergedLocalBeanDefinition(beanName); } // 是否是使用者定義的(不是 Spring 建立解析出來的) boolean synthetic = (mbd != null && mbd.isSynthetic()); // <4.3> **【核心】**通過 FactoryBean 獲得目標物件,單例模式會快取在 `factoryBeanObjectCache` 中 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; } ``` 過程如下: 1. 若 `name` 以 `&` 開頭,說明想要獲取 FactoryBean,則校驗其**正確性** 1. 如果是 NullBean 空物件,則直接返回 2. 如果不是 FactoryBean 型別,則丟擲異常 2. 如果 `beanInstance` 不是 FactoryBean 型別,不需要再處理則**直接返回**;或者(表示是 FactoryBean 型別) `name` 以 `&` 開頭,表示你想要獲取實際 FactoryBean 物件,則**直接返回**;還不符合條件的話,表示是 FactoryBean,需要獲取 getObject() 返回目標物件,**往下處理** 3. 如果入參沒有傳 BeanDefinition,則從 `factoryBeanObjectCache` 快取中獲取對應的 Bean 物件,如下: ```java // FactoryBeanRegistrySupport.java protected Object getCachedObjectForFactoryBean(String beanName) { return this.factoryBeanObjectCache.get(beanName); } ``` `factoryBeanObjectCache`:FactoryBean#getObject() 呼叫一次後返回的目標物件快取在這裡 入參傳了 BeanDefinition 表示這個 Bean 是剛建立的,不走快取,需要呼叫其 getObject() 方法獲取目標物件 4. 若第 `3` 步獲取的物件為空,則需要呼叫 FactoryBean#getObject() 獲得物件 1. 將 `beanInstance` 轉換成 FactoryBean 型別 2. 如果入參沒有傳 BeanDefinition 並且當前容器存在對應的 BeanDefinition,則獲取對應的 RootBeanDefinition(合併後) 3. **【核心】**通過 FactoryBean 獲得目標物件,單例模式會快取在 `factoryBeanObjectCache` 中,呼叫 `getObjectFromFactoryBean(FactoryBean, String, boolean)` 方法 #### getObjectFromFactoryBean 方法 `getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess)` 方法,獲取 FactoryBean 的目標物件,方法如下: ```java // FactoryBeanRegistrySupport.java protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) { // <1> `factory` 為單例模式,且單例 Bean 快取中存在 `beanName` 對應的 FactoryBean 物件 if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { // <1.1> 獲取單例鎖,保證安全 // <1.2> 從 `factoryBeanObjectCache` 快取中獲取 FactoryBean#getObject() 建立的目標物件 Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { // <1.3> 則根據 `factory` 獲取目標物件,呼叫 FactoryBean#getObject() 方法 object = doGetObjectFromFactoryBean(factory, beanName); // Only post-process and store if not put there already during getObject() call above // (e.g. because of circular reference processing triggered by custom getBean calls) // <1.4> 這裡再進行一次校驗,看是否在快取中存在 FactoryBean 建立的目標物件,如果有則優先從快取中獲取 // 保證 FactoryBean#getObject() 只能被呼叫一次 // 沒有的話,則對剛獲取到的目標物件進行接下來的處理 Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { // <1.5> 是否需要後續處理,這個 FactoryBean 的前身 BeanDefinition 是否由 Spring 解析出來的,通常情況下都是 if (shouldPostProcess) { // <1.5.1> 若該 FactoryBean 處於建立中,則直接返回這個目標物件,不進行接下來的處理過程 if (isSingletonCurrentlyInCreation(beanName)) { // Temporarily return non-post-processed object, not storing it yet.. return object; } // <1.5.2> 前置處理,將 `beanName` 標誌為正在建立 beforeSingletonCreation(beanName); try { // <1.5.3> 對通過 FactoryBean 獲取的目標物件進行後置處理 // 遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理) object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } finally { // <1.5.4> 後置處理,將 `beanName` 標誌為不在建立中 afterSingletonCreation(beanName); } } // <1.6> 如果快取中存在 `beanName` 對應的 FactoryBean 物件 // 上面不是判斷了嗎?也可能在上面的處理過程會有所變化,所以這裡在做一層判斷 // 目的:快取 FactoryBean 建立的目標物件,則需要保證 FactoryBean 本身這個物件存在快取中 if (containsSingleton(beanName)) { // <1.6.1> 將這個 FactoryBean 建立的目標物件儲存至 `factoryBeanObjectCache` this.factoryBeanObjectCache.put(beanName, object); } } } // <1.7> 返回 FactoryBean 建立的目標物件 return object; } } // <2> `factory` 非單例模式,或單例 Bean 快取中不存在 `beanName` 對應的 FactoryBean 物件 else { // <2.1> 則根據 `factory` 獲取目標物件,呼叫 FactoryBean#getObject() 方法 Object object = doGetObjectFromFactoryBean(factory, beanName); // <2.2> 是否需要後續處理,這個 FactoryBean 的前身 BeanDefinition 是否由 Spring 解析出來的,通常情況下都是 if (shouldPostProcess) { try { // <2.2.1> 對通過 FactoryBean 獲取的目標物件進行後置處理 // 遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理) object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } // <2.3> 返回 FactoryBean 建立的目標物件,非單例模式不會進行快取 return object; } } ``` 過程如下: 1. `factory` 為單例模式,且單例 Bean 快取中存在 `beanName` 對應的 FactoryBean 物件 1. 獲取單例鎖,保證安全 2. 從 `factoryBeanObjectCache` 快取中獲取 FactoryBean#getObject() 建立的目標物件 3. 則根據 `factory` 獲取目標物件,呼叫 FactoryBean#getObject() 方法(反射機制) 4. 這裡再進行一次校驗(第 `1.2` 已經判斷過),看是否在快取中存在 FactoryBean 建立的目標物件,如果有則優先從快取中獲取,保證 FactoryBean#getObject() 只能被呼叫一次;沒有的話,則對剛獲取到的目標物件進行接下來的處理 5. 是否需要後續處理,這個 FactoryBean 的 BeanDefinition 是否由 Spring 解析出來的,通常情況下都是 1. 若該 FactoryBean 處於建立中,則直接返回這個目標物件,不進行接下來的處理過程 2. 前置處理,將 `beanName` 標誌為正在建立 3. 對通過 FactoryBean 獲取的目標物件進行後置處理,遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理) 4. 後置處理,將 `beanName` 標誌為不在建立中 5. 如果快取中存在 `beanName` 對應的 FactoryBean 物件,上面不是判斷了嗎(第 `1` 步判斷過)? 也可能在上面的處理過程會有所變化,所以這裡在做一層判斷,目的:快取 FactoryBean 建立的目標物件,則需要保證 FactoryBean 本身這個物件存在快取中 1. 將這個 FactoryBean 建立的目標物件儲存至 `factoryBeanObjectCache` 6. 返回 FactoryBean 建立的目標物件 2. `factory` 非單例模式,或單例 Bean 快取中不存在 `beanName` 對應的 FactoryBean 物件 1. 則根據 `factory` 獲取目標物件,呼叫 FactoryBean#getObject() 方法(反射機制) 2. 是否需要後續處理,這個 FactoryBean 的 BeanDefinition 是否由 Spring 解析出來的,通常情況下都是 1. 對通過 FactoryBean 獲取的目標物件進行後置處理,遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理) 3. 返回 FactoryBean 建立的目標物件,非單例模式不會進行快取 **概括**:呼叫 FactoryBean#getObject() 獲取目標物件,單例模式會快取起來;過程中 Sping 考慮到各種情況,例如保證單例模式下 FactoryBean#getObject() 只調用一次,是否需要進行後置處理。 ### 4. 非單例模式依賴檢查 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) // 相等 || (curVal instanceof Set && ((Set) curVal).contains(beanName)))); // 包含 } ``` `prototypesCurrentlyInCreation` 中儲存非單例模式下正在建立的 Bean 的名稱,這裡又重新建立,表示出現迴圈依賴,則直接丟擲異常 Spring 對於非單例模式的 Bean 無法進行相關快取,也就無法處理迴圈依賴的情況,選擇了直接丟擲異常 ### 5. BeanFactory 層次性載入 Bean 策略 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 BeanFactory parentBeanFactory = getParentBeanFactory(); // <5> 如果從當前容器中沒有找到對應的 BeanDefinition,則從父容器中載入(如果存在父容器) if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. // <5.1> 獲取 `beanName`,因為可能是別名,則進行處理 // 和第 `1` 步不同,不需要對 `&` 進行處理,因為進入父容器重新依賴查詢 String nameToLookup = originalBeanName(name); // <5.2> 若為 AbstractBeanFactory 型別,委託父容器的 doGetBean 方法進行處理 // 否則,就是非 Spring IoC 容器,根據引數呼叫相應的 `getBean(...)`方法 if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } ``` 如果**當前** BeanFactory 沒有對應的 BeanDefinition,也就無法建立 Bean,但是如果存在**父** BeanFactory,則將接下來的操作交由**父** BeanFactory,找不到會層層找上去,如果所有 BeanFactory 都找不到對應的 BeanDefinition 最終會丟擲異常。 ### 6. 將 beanName 標記為已建立 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 // <6> 如果不是僅僅做型別檢查,則表示需要建立 Bean,將 `beanName` 標記為已建立過 if (!typeCheckOnly) { markBeanAsCreated(beanName); } ``` 如果不是僅僅做型別檢查,則呼叫 `markBeanAsCreated(String beanName)` 方法,如下: ```java // AbstractBeanFactory.java protected void markBeanAsCreated(String beanName) { // 沒有建立 if (!this.alreadyCreated.contains(beanName)) { // 加上全域性鎖 synchronized (this.mergedBeanDefinitions) { // 再次檢查一次:DCL 雙檢查模式 if (!this.alreadyCreated.contains(beanName)) { // Let the bean definition get re-merged now that we're actually creating // the bean... just in case some of its metadata changed in the meantime. // 從 mergedBeanDefinitions 中刪除 beanName,並在下次訪問時重新建立它 clearMergedBeanDefinition(beanName); // 新增到已建立 bean 集合中 this.alreadyCreated.add(beanName); } } } } ``` 將這個 `beanName` 儲存在 `alreadyCreated` 集合中(SetFromMap),在後面的**迴圈依賴檢查**中會使用到 ### 7. 獲取 RootBeanDefinition 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 // <7> 從容器中獲取 `beanName` 對應的的 RootBeanDefinition(合併後) final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // 檢查是否為抽象類 checkMergedBeanDefinition(mbd, beanName, args); ``` 因為我們定義的 Bean 大多數都被 Spring 解析成 GenericBeanDefinition 型別,具有父子關係,則需要獲取最終的 BeanDefinition;如果存在父子關係,則會進行一系列的合併,轉換成 RootBeanDefinition 物件,呼叫 `getMergedLocalBeanDefinition(String beanName)` 方法,如下: ```java // AbstractBeanFactory.java protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { // Quick check on the concurrent map first, with minimal locking. // 從 `mergedBeanDefinitions` 快取中獲取合併後的 RootBeanDefinition,存在則直接返回 RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); if (mbd != null) { return mbd; } // 獲取 BeanDefinition 並轉換成,如果存在父子關係則進行合併 return getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); } protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException { return getMergedBeanDefinition(beanName, bd, null); } protected RootBeanDefinition getMergedBeanDefinition( String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException { // 加鎖 synchronized (this.mergedBeanDefinitions) { RootBeanDefinition mbd = null; // Check with full lock now in order to enforce the same merged instance. if (containingBd == null) { mbd = this.mergedBeanDefinitions.get(beanName); } if (mbd == null) { // 如果沒有父類則直接轉換成 RootBeanDefinition 物件 if (bd.getParentName() == null) { // Use copy of given root bean definition. if (bd instanceof RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { mbd = new RootBeanDefinition(bd); } } // 有父類則進行合併 else { // Child bean definition: needs to be merged with parent. BeanDefinition pbd; try { // 獲取父類的對應的 BeanDefinition 物件 String parentBeanName = transformedBeanName(bd.getParentName()); if (!beanName.equals(parentBeanName)) { pbd = getMergedBeanDefinition(parentBeanName); } else { BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent"); } } } catch (NoSuchBeanDefinitionException ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex); } // Deep copy with overridden values. mbd = new RootBeanDefinition(pbd); // 父子合併 mbd.overrideFrom(bd); } // Set default singleton scope, if not configured before. if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON); } // A bean contained in a non-singleton bean cannot be a singleton itself. // Let's correct this on the fly here, since this might be the result of // parent-child merging for the outer bean, in which case the original inner bean // definition will not have inherited the merged outer bean's singleton status. if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } // Cache the merged bean definition for the time being // (it might still get re-merged later on in order to pick up metadata changes) if (containingBd == null && isCacheBeanMetadata()) { // 放入快取中 this.mergedBeanDefinitions.put(beanName, mbd); } } return mbd; } } ``` 過程大致如下: 1. 從 `mergedBeanDefinitions` 快取中獲取合併後的 RootBeanDefinition,存在則直接返回,不存在則進行後面的操作 2. 獲取合併後的 RootBeanDefinition 物件,邏輯並不複雜,將一些屬性進行合併;這裡對於**父** BeanDefinition 的獲取也存在層次性查詢策略;注意,如果一個單例 BeanDefinition 包含在非單例 BeanDefinition,那麼會變成非單例 Bean 後續還會對合並後的 RootBeanDefinition 物件進行檢查,如果是抽象的,則丟擲異常 ### 8. 依賴 Bean 的處理 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 // Guarantee initialization of beans that the current bean depends on. // <8> 獲取當前正在建立的 Bean 所依賴物件集合(`depends-on` 配置的依賴) String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // <8.1> 檢測是否存在迴圈依賴,存在則丟擲異常 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // <8.2> 將 `beanName` 與 `dep` 之間依賴的關係進行快取 registerDependentBean(dep, beanName); try { // <8.3> 先建立好依賴的 Bean(重新呼叫 `getBean(...)` 方法) getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } ``` - 每個 Bean 不一定是單獨工作的,可以通過 `depends-on` 配置依賴的 Bean,其他 Bean 也可以依賴它 - 對於依賴的 Bean,會優先載入,所以在 Spring 的載入順序中,在初始化某個 Bean 的時候,首先會初始化這個 Bean 的依賴 #### isDependent 方法 在初始化依賴的 Bean 之前,會呼叫 `isDependent(String beanName, String dependentBeanName)` 方法,判斷是否出現迴圈依賴,方法如下: ```java DefaultSingletonBeanRegistry.java protected boolean isDependent(String beanName, String dependentBeanName) { synchronized (this.dependentBeanMap) { return isDependent(beanName, dependentBeanName, null); } } private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set alreadySeen) { // <1> `alreadySeen` 中已經檢測過該 `beanName` 則直接返回 `false` if (alreadySeen != null && alreadySeen.contains(beanName)) { return false; } // <2> 獲取最終的 `beanName`,因為可能是別名,需要進行相關處理 String canonicalName = canonicalName(beanName); // <3> 從 `dependentBeanMap` 中獲取依賴 `beanName` 的 Bean 集合 Set dependentBeans = this.dependentBeanMap.get(canonicalName); // <4> 沒有 Bean 依賴該 `beanName`,也就不存在迴圈依賴,返回 `false` if (dependentBeans == null) { return false; } // <5> 依賴 `beanName` 的 Bean 們包含 `dependentBeanName`,表示出現迴圈依賴,返回 `true` if (dependentBeans.contains(dependentBeanName)) { // `beanName` 與 `dependentBeanName` 相互依賴 return true; } // <6> 對依賴該 `beanName` 的 Bean 們進行檢查,看它們是否與 `dependentBeanName` 存在依賴,遞迴處理 for (String transitiveDependency : dependentBeans) { if (alreadySeen == null) { alreadySeen = new HashSet<>(); } alreadySeen.add(beanName); if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) { return true; } } return false; } ``` 過程大致如下: 1. `alreadySeen` 中已經檢測過該 `beanName` 則直接返回 `false` 2. 獲取最終的 `beanName`,因為可能是別名,需要進行相關處理 3. 從 `dependentBeanMap` 中獲取依賴 `beanName` 的 Bean 集合 4. 沒有 Bean 依賴該 `beanName`,也就不存在迴圈依賴,返回 `false` 5. 依賴 `beanName` 的 Bean 們包含 `dependentBeanName`,表示出現迴圈依賴,返回 `true` 6. 對依賴該 `beanName` 的 Bean 們進行檢查,看它們是否與 `dependentBeanName` 存在依賴,遞迴處理 判斷是否出現迴圈依賴的過程有點繞,需要花點時間理解一下。例如:現在檢查 A ->(依賴)B,看是否出現迴圈依賴,我獲取到依賴 A 的所有 Bean,看 B 是否依賴這裡面的 Bean,如果出現 A -> B -> C -> A,那就出現迴圈依賴了。如果出現**迴圈依賴**,則會**丟擲異常**,所以我們說 Spring 處理了單例 Bean 的**迴圈依賴注入**比較好一點。 #### registerDependentBean 方法 將 `beanName` 與 `dep`(`beanName` 的依賴)之間依賴的關係進行快取,呼叫 `registerDependentBean(String beanName, String dependentBeanName)` 方法,如下: ```java DefaultSingletonBeanRegistry.java public void registerDependentBean(String beanName, String dependentBeanName) { String canonicalName = canonicalName(beanName); // 對應關係:beanName -> 依賴 beanName 的集合 synchronized (this.dependentBeanMap) { Set dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8)); if (!dependentBeans.add(dependentBeanName)) { return; } } // 對應關係:beanName - > beanName 的依賴的集合 synchronized (this.dependenciesForBeanMap) { Set dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8)); dependenciesForBean.add(canonicalName); } } ``` 將兩者的依賴關係儲存起來,目的是在 `isDependent` 方法中判斷是否出現迴圈依賴 #### getBean 方法 載入 `beanName` 依賴的 Bean,同樣是呼叫 `AbstractBeanFactory#getBean(String dep)` 方法,也就是本文開頭講的這個方法 ### 9. 不同作用域的 Bean 的建立 Spring 的作用域劃分為三種:單例模式、原型模式、其他模式,會依次進行判斷,然後進行建立,建立過程都是一樣的,主要是儲存範圍不一樣 - 單例模式:一個 BeanFactory 有且僅有一個例項 - 原型模式:每次依賴查詢和依賴注入生成新 Bean 物件 - 其他模式,例如 `request` 作用域會將 Bean 儲存在 ServletRequest 上下文中;`session` 作用域會將 Bean 儲存在 HttpSession 中;`application` 作用域會將 Bean 儲存在 ServletContext 中 #### 單例模式 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 if (mbd.isSingleton()) { // <9.1> 單例模式 /* * <9.1.1> 建立 Bean,成功建立則進行快取,並移除快取的早期物件 * 建立過程實際呼叫的下面這個 `createBean(...)` 方法 */ sharedInstance = getSingleton(beanName, // ObjectFactory 實現類 () -> { try { // **【核心】** 建立 Bean return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. // 如果建立過程出現異常,則顯式地從快取中刪除當前 Bean 相關資訊 // 在單例模式下為了解決迴圈依賴,建立過程會快取早期物件,這裡需要進行刪除 destroySingleton(beanName); throw ex; } }); // <9.1.2> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回 // 否則,呼叫 FactoryBean#getObject() 獲取目標物件 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } ``` 如果是單例模式,建立過程大致如下: 1. 呼叫 `DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory singletonFactory)` 方法 建立 Bean,成功建立則進行快取,並移除快取的早期物件,建立過程實際呼叫的下面這個 `AbstractAutowireCapableBeanFactory#createBean(...)` 方法 2. FactoryBean 的處理,在前面 **3. FactoryBean 的處理** 中已經分析過 ##### getSingleton 方法 `DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory singletonFactory)` 方法,單例模式下獲取單例 Bean,如下: ```java // DefaultSingletonBeanRegistry.java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); // 全域性加鎖 synchronized (this.singletonObjects) { // <1> 從 `singletonObjects` 單例 Bean 的快取中獲取 Bean(再檢查一遍),存在則直接返回,否則開始建立 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } // <2> 將 `beanName` 標記為單例模式正在建立 beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { /** * <3> 建立 Bean,實際呼叫 * {@link AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])} 方法 */ singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } // <4> 將 `beanName` 標記為不在建立中,照應第 `2` 步 afterSingletonCreation(beanName); } // <5> 如果這裡是新建立的單例模式 Bean,則在 `singletonObjects` 中進行快取(無序),移除快取的早期物件 // 並在 `registeredSingletons` 中儲存 `beanName`,保證註冊順序 if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } } ``` 過程大致如下: 1. 從 `singletonObjects` 單例 Bean 的快取中獲取 Bean(再檢查一遍),存在則直接返回,否則開始建立 2. 將 `beanName` 標記為單例模式正在建立 3. **【核心】**建立 Bean,實際呼叫 `AbstractAutowireCapableBeanFactory#createBean(...)` 方法 4. 將 `beanName` 標記為不在建立中,照應第 `2` 步 5. 如果這裡是新建立的單例模式 Bean,則在 `singletonObjects` 中進行快取(無序),移除快取的早期物件,並在 `registeredSingletons` 中儲存 `beanName`,保證註冊順序 ##### createBean 方法 `AbstractAutowireCapableBeanFactory#createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)` 方法,建立 Bean,整個過程大致如下: 1. Bean 的例項化 2. 屬性賦值(包括依賴注入) 3. Aware 介面回撥 4. 呼叫初始化方法 上面涉及到 Bean 生命週期的大部分階段,將會在後續的文章中依次分析 #### 原型模式 對應程式碼段: ```java // AbstractBeanFactory.java // <9.2> 原型模式 else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { // <9.2.1> 將 `beanName` 標記為**非單例模式**正在建立 beforePrototypeCreation(beanName); // <9.2.2> **【核心】** 建立 Bean prototypeInstance = createBean(beanName, mbd, args); } finally { // <9.2.3> 將 `beanName` 標記為不在建立中,照應第 `9.2.1` 步 afterPrototypeCreation(beanName); } // <9.2.4> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回 // 否則,呼叫 FactoryBean#getObject() 獲取目標物件 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } ``` 過程大致如下: 1. 將 `beanName` 標記為**非單例模式**正在建立 2. **【核心】**建立 Bean 也是呼叫 `AbstractAutowireCapableBeanFactory#createBean(...)` 方法,這裡沒有快取,每次載入 Bean 都會建立一個物件 3. 將 `beanName` 標記為不在建立中,照應第 `1` 步 4. FactoryBean 的處理,在前面 **3. FactoryBean 的處理** 中已經分析過 #### 其他模式 對應程式碼段: ```java // AbstractBeanFactory.java // <9.3> 其他模式 else { // <9.3.1> 獲取該模式的 Scope 物件 `scope`,不存在則丟擲異常 String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { // <9.3.2> 從 `scope` 中獲取 `beanName` 對應的物件(看你的具體實現),不存在則執行**原型模式**的四個步驟進行建立 Object scopedInstance = scope.get(beanName, () -> { // 將 `beanName` 標記為**非單例模式**式正在建立 beforePrototypeCreation(beanName); try { // **【核心】** 建立 Bean return createBean(beanName, mbd, args); } finally { // 將 `beanName` 標記為不在建立中,照應上一步 afterPrototypeCreation(beanName); } }); // 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回 // 否則,呼叫 FactoryBean#getObject() 獲取目標物件 bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } ``` 過程如下: 1. 獲取該模式的 Scope 物件 `scope`,不存在則丟擲異常 2. 從 `scope` 中獲取 `beanName` 對應的物件(看你的具體實現),不存在則執行**原型模式**的四個步驟進行建立 想要自定義一個作用域,可以實現 `org.springframework.beans.factory.config.Scope` 介面,並往 Spring 應用上下文註冊即可 ### 10. 型別轉換 對應程式碼段: ```java // AbstractBeanFactory#doGetBean(...) 方法 // <10> 如果入參 `requiredType` 不為空,並且 Bean 不是該型別,則需要進行型別轉換 if (requiredType != null && !requiredType.isInstance(bean)) { try { // <10.1> 通過型別轉換機制,將 Bean 轉換成 `requiredType` 型別 T convertedBean = getTypeConvert