1. 程式人生 > >死磕Spring之IoC篇 - BeanDefinition 的解析階段(XML 檔案)

死磕Spring之IoC篇 - BeanDefinition 的解析階段(XML 檔案)

> 該系列文章是本人在學習 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) ## BeanDefinition 的解析階段(XML 檔案) 上一篇文章[**《BeanDefinition 的載入階段(XML 檔案)》**](https://www.cnblogs.com/lifullmoon/p/14437305.html)獲取到 `org.w3c.dom.Document` 物件後,需要通過 DefaultBeanDefinitionDocumentReader 進行解析,解析出 XML 檔案中定義的 BeanDefinition 並進行註冊,先來回顧一下上一篇文章中的這段程式碼: ```java public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // <1> 建立 BeanDefinitionDocumentReader 物件 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // <2> 獲取已註冊的 BeanDefinition 數量 int countBefore = getRegistry().getBeanDefinitionCount(); // <3> 建立 XmlReaderContext 物件(讀取 Resource 資源的上下文物件) // <4> 根據 Document、XmlReaderContext 解析出所有的 BeanDefinition 並註冊 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // <5> 計算新註冊的 BeanDefinition 數量 return getRegistry().getBeanDefinitionCount() - countBefore; } ``` 本文開始分析第 `4` 步,BeanDefinition 的解析階段,其中 BeanDefinitionDocumentReader 只有 DefaultBeanDefinitionDocumentReader 一個預設實現類 ### BeanDefinitionDocumentReader 介面 `org.springframework.beans.factory.xml.BeanDefinitionDocumentReader`,解析 DOM document 中的 BeanDefinition 並註冊,程式碼如下: ```java public interface BeanDefinitionDocumentReader { /** * Read bean definitions from the given DOM document and * register them with the registry in the given reader context. * @param doc the DOM document * @param readerContext the current context of the reader * (includes the target registry and the resource being parsed) * @throws BeanDefinitionStoreException in case of parsing errors */ void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) throws BeanDefinitionStoreException; } ``` ### DefaultBeanDefinitionDocumentReader `org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader`,Spring 預設的 BeanDefinitionDocumentReader 實現類,從 XML 檔案中解析出 BeanDefinition 並註冊 #### 建構函式 ```java public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader { /** bean */ public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT; public static final String NESTED_BEANS_ELEMENT = "beans"; public static final String ALIAS_ELEMENT = "alias"; public static final String NAME_ATTRIBUTE = "name"; public static final String ALIAS_ATTRIBUTE = "alias"; public static final String IMPORT_ELEMENT = "import"; public static final String RESOURCE_ATTRIBUTE = "resource"; public static final String PROFILE_ATTRIBUTE = "profile"; @Nullable private XmlReaderContext readerContext; /** * XML 檔案的 BeanDefinition 解析器 */ @Nullable private BeanDefinitionParserDelegate delegate; } ``` 上面定義了 XML 檔案中常用的標籤 #### 1. registerBeanDefinitions 方法 `registerBeanDefinitions(Document doc, XmlReaderContext readerContext)` 方法,根據 Document、XmlReaderContext 解析出所有的 BeanDefinition 並註冊,方法如下: ```java @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; // 獲得 XML Document Root Element // 執行註冊 BeanDefinition doRegisterBeanDefinitions(doc.getDocumentElement()); } /** * Register each bean definition within the given root {@code
} element. */ @SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...) protected void doRegisterBeanDefinitions(Element root) { // 記錄老的 BeanDefinitionParserDelegate 物件,避免再次呼叫當前方法時解析出現問題(預設值可能不同) BeanDefinitionParserDelegate parent = this.delegate; // <1> 建立 BeanDefinitionParserDelegate 物件 `delegate`,並初始化預設值 this.delegate = createDelegate(getReaderContext(), root, parent); // <2> 檢查
根標籤的名稱空間是否為空,或者是 http://www.springframework.org/schema/beans if (this.delegate.isDefaultNamespace(root)) { // <2.1> 獲取 `profile` 屬性 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { // <2.2> 使用分隔符切分,可能有多個 `profile` String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. // <2.3> 根據 Spring Environment 進行校驗,如果所有 `profile` 都無效,則不進行註冊 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } // <3> 解析前處理 preProcessXml(root); // <4> 解析出 XML Document 中的 BeanDefinition 並註冊 parseBeanDefinitions(root, this.delegate); // <5> 解析後處理 postProcessXml(root); // 設定 delegate 回老的 BeanDefinitionParserDelegate 物件 this.delegate = parent; } ``` 首先獲取 XML Document 的最頂層的標籤,也就是 `
`,然後對其子標籤進行解析,這裡的過程大致如下: 1. 建立 BeanDefinitionParserDelegate 物件 `delegate`,並初始化預設值 2. 檢查 `` 根標籤是否是預設名稱空間(xmlns 屬性,為空或者是 `http://www.springframework.org/schema/beans`),是的話進行校驗 1. 獲取 `profile` 屬性,使用分隔符切分 2. 根據 Spring Environment 進行校驗,如果所有 `profile` 都無效,則不進行註冊 3. 解析前處理,空方法,暫時忽略 4. 解析出 XML Document 中的 BeanDefinition 並註冊,呼叫 `parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)` 方法 5. 解析後處理,空方法,暫時忽略 #### 2. parseBeanDefinitions 方法 `parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)` 方法,解析 XML Document 的最頂層的標籤,解析出 BeanDefinition 並註冊,方法如下: ```java protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // <1> 如果根節點使用預設名稱空間,執行預設解析 if (delegate.isDefaultNamespace(root)) { // <1.1> 遍歷所有的子節點 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // <1.2> 如果該節點使用預設名稱空間,執行預設解析 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } // <1.3> 如果該節點非預設名稱空間,執行自定義解析 else { delegate.parseCustomElement(ele); } } } } // <2> 如果根節點非預設名稱空間,執行自定義解析 else { delegate.parseCustomElement(root); } } ``` 解析過程大致如下: 1. 如果根節點使用預設名稱空間,執行預設解析 1. 遍歷所有的子節點 2. 如果該節點使用預設名稱空間,執行預設解析,呼叫 `parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)` 方法 3. 如果該節點非預設名稱空間,執行自定義解析,呼叫 `BeanDefinitionParserDelegate#parseCustomElement(Element ele)` 方法 2. 如果根節點非預設名稱空間,執行自定義解析,呼叫 `BeanDefinitionParserDelegate#parseCustomElement(Element ele)` 方法 ------ **名稱空間是什麼?** ```xml ``` 在 XML 檔案中的標籤的 `xmlns` 可以定義預設的名稱空間,`xmlns:context` 定義 `context` 的名稱空間,`xsi:schemaLocation` 定義了名稱空間對應的 XSD 檔案(校驗 XML 內容)。 上面的 `` 和 `` 標籤的名稱空間為 `http://www.springframework.org/schema/beans`,其中 `` 標籤的名稱空間為 `http://www.springframework.org/schema/context`(不是預設名稱空間) ------ 本文主要分析預設名稱空間的解析過程,其他名稱空間(註解相關)在後面進行分析,基於 Extensible XML authoring 擴充套件 Spring XML 元素會進入這裡,主要是通過 NamespaceHandler 這個介面實現的。例如 Spring 整合 Mybatis 專案中的 `` 就是擴充套件 Spring XML 元素,具體實現在後面分析;Spring 的`` 最終會通過 ComponentScanBeanDefinitionParser 進行解析。**ComponentScanBeanDefinitionParser** 是將 @Component 註解標註的類轉換成 BeanDefinition 的解析器。 #### 3. parseDefaultElement 方法 `parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)` 方法,處理預設名稱空間的節點,方法如下: ```java private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 解析 `` importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 解析 ``,將 name 對應的 alias 別名進行註冊 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // 解析 `` processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // 迴圈處理,解析 `` doRegisterBeanDefinitions(ele); } } ``` 解析下面四種標籤: - `` 標籤,例如這麼配置:``,那麼這裡會獲取到對應的 XML 檔案,然後進行相同的處理過程 - `` 標籤,將 name 對應的 alias 別名進行註冊,往 AliasRegistry 註冊(BeanDefinitionRegistry 繼承了它),也就是說你可以通過別名找到對應的 Bean - `` 標籤,解析成 BeanDefinition 並註冊,呼叫 `processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)` 方法 - `` 標籤,迴圈處理,和前面的步驟相同 #### 4. processBeanDefinition 方法 `processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)` 方法,將 `` 標籤解析成 BeanDefinition 並註冊,方法如下: ```java protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // <1> 解析 `` 標籤,返回 BeanDefinitionHolder 物件(包含 BeanDefinition、beanName、aliases) BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // <2> 對該標籤進行裝飾,一般不會,暫時忽略 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. // <3> 進行 BeanDefinition 的註冊 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. // <4> 發出響應事件,通知相關的監聽器,已完成該 Bean 標籤的解析 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } } ``` 過程如下: 1. 解析 `` 標籤,返回 BeanDefinitionHolder 物件(包含 BeanDefinition、beanName、aliases),呼叫 `BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element ele)` 方法 2. 對該標籤進行裝飾,和上面處理自定義標籤類似,暫時忽略 3. 進行 BeanDefinition 的註冊 4. 發出響應事件,通知相關的監聽器,已完成該 Bean 標籤的解析 ### BeanDefinitionParserDelegate `org.springframework.beans.factory.xml.BeanDefinitionParserDelegate`,解析 XML Document 裡面的 BeanDefinition #### 5. parseBeanDefinitionElement 方法 `parseBeanDefinitionElement(Element ele)` 方法,將 XML Document 裡面的某個標籤解析成 BeanDefinition,方法如下: ```java @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); } public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // <1> 計算 BeanDefinition 的 `beanName` 名稱和 `aliases` 別名集合 // <1.1> 獲取標籤的 `id` 和 `name` 屬性 String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // <1.2> 將 `name` 屬性全部新增至別名集合 List aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } // <1.3> 設定 Bean 的名稱,優先 `id` 屬性,其次 `name` 屬性 String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); // 移除出別名集合 if (logger.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } // <1.4> 檢查 `beanName` 的唯一性 if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // <2> 解析 `` 標籤相關屬性,構造出一個 GenericBeanDefinition 物件 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { // <3> 如果不存在 `beanName`,則根據 Class 物件的名稱生成一個 if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { // 內部 Bean // <3.1> 生成唯一的 `beanName` beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { // <3.2> 生成唯一的 beanName beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } // <4> 建立 BeanDefinitionHolder 物件,設定 `beanName` 名稱和 `aliases` 別名集合,返回 String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; } ``` 過程如下: 1. 計算 BeanDefinition 的 `beanName` 名稱和 `aliases` 別名集合 1. 獲取標籤的 `id` 和 `name` 屬性 2. 將 `name` 屬性全部新增至別名集合 `aliases` 3. 設定 Bean 的名稱 `beanName`,優先 `id` 屬性,其次 `name` 屬性 4. 檢查 `beanName` 的唯一性 2. 解析 `` 標籤相關屬性,構造出一個 GenericBeanDefinition 物件,呼叫 `parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)` 方法 3. 如果不存在 `beanName`,則根據 Class 物件的名稱生成一個 4. 建立 BeanDefinitionHolder 物件,設定 `beanName` 名稱和 `aliases` 別名集合,返回 #### 6. parseBeanDefinitionElement 過載方法 `parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)` 方法,解析 `` 成 GenericBeanDefinition 物件,方法如下: ```java @Nullable public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); // <1> 獲取 `class` 和 `parent` 屬性 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { // <2> 構建一個 GenericBeanDefinition 物件 `bd` AbstractBeanDefinition bd = createBeanDefinition(className, parent); // <3> 解析 `` 的各種屬性並賦值 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); // 提取 description bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // <4> 解析 `` 的子標籤,生成的物件設定到 `bd` 中 // <4.1> 解析 `` 元資料標籤 parseMetaElements(ele, bd); // <4.2> 解析 `` 標籤 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // <4.3> 解析 `` 標籤 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // <4.4> 解析 `` 建構函式的引數集合標籤 parseConstructorArgElements(ele, bd); // <4.5> 解析 `` 屬性標籤 parsePropertyElements(ele, bd); // <4.5> 解析 `` 標籤 parseQualifierElements(ele, bd); // <5> 設定 Bean 的 `resource` 資源為 XML 檔案資源 bd.setResource(this.readerContext.getResource()); // <6> 設定 Bean 的 `source` 來源為 `` 標籤物件 bd.setSource(extractSource(ele)); return bd; } // ... 省略 catch 各種異常 finally { this.parseState.pop(); } return null; } ``` 過程如下: 1. 獲取 `class` 和 `parent` 屬性 2. 構建一個 GenericBeanDefinition 物件 `bd`,設定 `parentName` 和 `beanClass`(Class 物件)或者 `className`(Class 名稱) 3. 解析 `` 的各種屬性並賦值:scope、abstract、lazy-init、autowire、depends-on、autowire-candidate、primary、init-method、destroy-method、factory-method 4. 解析 `` 的子標籤,生成的物件設定到 `bd` 中 1. 解析 `` 元資料標籤,將 key-value 儲存至 Map 中 2. 解析 `` 標籤,解析成 LookupOverride 物件,用於實現 Bean 中的某個方法 3. 解析 `` 標籤,解析成 ReplaceOverride 物件,用於替換 Bean 中的某個方法 4. 解析 `` 建構函式的引數集合標籤,將各個引數解析出來,可根據 index 屬性進行排序 5. 解析 `` 屬性標籤,將各個屬性解析出來,每個屬性對應一個 PropertyValue,新增至 `bd` 的 MutablePropertyValues 屬性中 6. 解析 `` 標籤,解析出需要注入的物件 AutowireCandidateQualifier 5. 設定 Bean 的 `resource` 資源為 XML 檔案資源 6. 設定 Bean 的 `source` 來源為 `` 標籤物件 > lookup-method,會被解析成 LookupOverride 物件,replaced-method 會被解析成 ReplaceOverride 物件,這兩個標籤不是很常用 > > lookup-method:例如一個在一個抽象類中定義了抽象方法,可以通過這個標籤定義一個 Bean 實現這個方法,Spring 會動態改變抽象類該方法的實現 > > replace-method:通過這個標籤定義一個 Bean,去覆蓋對應的方法,Spring 會動態替換類的這個方法 ### BeanDefinitionReaderUtils `org.springframework.beans.factory.support.BeanDefinitionReaderUtils`,BeanDefinition 的解析過程中的工具類 #### 7. registerBeanDefinition 方法 `registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)`,註冊 BeanDefinition,方法如下: ```java public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // <1> 註冊 BeanDefinition // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // <2> 註冊 alias 別名 // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } ``` 過程如下: 1. 註冊 BeanDefinition,呼叫 `BeanDefinitionRegistry#registerBeanDefinition(String beanName, BeanDefinition beanDefinition)` 方法 2. 註冊 alias 別名,呼叫 `BeanDefinitionRegistry#registerAlias(String name, String alias)` 方法 這裡的 BeanDefinitionRegistry 實現類是 DefaultListableBeanFactory,它是 Spring 底層 IoC 容器,還繼承了 SimpleAliasRegistry(AliasRegistry 實現類) #### 8. 註冊 BeanDefinition ```java // org.springframework.beans.factory.support.DefaultListableBeanFactory @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // 校驗 beanName 與 beanDefinition 非空 Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); // <1> 校驗 BeanDefinition // 這是註冊前的最後一次校驗了,主要是對屬性 methodOverrides 進行校驗 if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } // <2> 從快取中獲取指定 beanName 的 BeanDefinition BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); // <3> 如果已經存在 if (existingDefinition != null) { // 如果存在但是不允許覆蓋,丟擲異常 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } // 覆蓋 beanDefinition 大於 被覆蓋的 beanDefinition 的 ROLE ,列印 info 日誌 else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } // 覆蓋 beanDefinition 與 被覆蓋的 beanDefinition 不相同,列印 debug 日誌 else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } // 其它,列印 debug 日誌 else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } // <4> 如果未存在 else { // 檢測建立 Bean 階段是否已經開啟,如果開啟了則需要對 beanDefinitionMap 進行併發控制 if (hasBeanCreationStarted()) { // beanDefinitionMap 為全域性變數,避免併發情況 // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { // 新增到 BeanDefinition 到 beanDefinitionMap 中 this.beanDefinitionMap.put(beanName, beanDefinition); // 新增 beanName 到 beanDefinitionNames 中 List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; // 從 manualSingletonNames 移除 beanName removeManualSingletonName(beanName); } } else { // 新增到 BeanDefinition 到 beanDefinitionMap 中 // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); // 新增 beanName 到 beanDefinitionNames 中,保證註冊順序 this.beanDefinitionNames.add(beanName); // 從 manualSingletonNames 移除 beanName removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } // <5> 重新設定 beanName 對應的快取 if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } } ``` 邏輯不復雜,主要是對 `beanName` 和該 BeanDefinition 物件的校驗,最終將其對映儲存在 `beanDefinitionMap` 中(ConcurrentHashMap),key 就是 `beanName` #### 9. 註冊 alias 別名 ```java // org.springframework.core.SimpleAliasRegistry @Override public void registerAlias(String name, String alias) { // 校驗 name 、 alias Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); synchronized (this.aliasMap) { // name == alias 則去掉alias if (alias.equals(name)) { this.aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name"); } } else { // 獲取 alias 已註冊的 beanName String registeredName = this.aliasMap.get(alias); // 已存在 if (registeredName != null) { // 相同,則 return ,無需重複註冊 if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } // 不允許覆蓋,則丟擲 IllegalStateException 異常 if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'"); } } // 校驗,是否存在迴圈指向 checkForAliasCircle(name, alias); // 註冊 alias this.aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'"); } } } } ``` 邏輯不復雜,將 `alias` 與 `beanName` 對映儲存至 `aliasMap` 中(ConcurrentHashMap) ### 總結 解析出 XML 檔案中的 BeanDefinition 並註冊的整個過程大致如下: 1. 根據 XSD 檔案對 XML 檔案進行校驗 2. 將 XML 檔案資源轉換成 `org.w3c.dom.Document` 物件 3. 根據 Document 物件解析 `` 標籤,遍歷所有的子標籤 1. 如果是子標籤是預設的名稱空間(為空或者 `http://www.springframework.org/schema/beans`)則進行處理,例如:``、``、``和``,其中 `` 會被解析出一個 **GenericBeanDefinition** 物件,然後進行註冊 2. 否則,找到對應的 **NamespaceHandler** 物件進行解析,例如:`` 、``、``,這些非預設名稱空間的標籤都會有對應的 **BeanDefinitionParser** 解析器 至此,我們通過 XML 檔案定義的 Bean 已經轉換成了 Bean 的“前身”,也就是 BeanDefinition 物件。接下來會分析在 XML 檔案中,非預設名稱空間的標籤是如何進行處