死磕Spring之IoC篇 – BeanDefinition 的解析階段(XML 文件)

該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關源碼,可能對讀者不太友好,請結合我的源碼注釋 Spring 源碼分析 GitHub 地址 進行閱讀

Spring 版本:5.1.14.RELEASE

開始閱讀這一系列文章之前,建議先查看《深入了解 Spring IoC(面試題)》這一篇文章

該系列其他文章請查看:《死磕 Spring 之 IoC 篇 – 文章導讀》

BeanDefinition 的解析階段(XML 文件)

上一篇文章《BeanDefinition 的載入階段(XML 文件)》獲取到 org.w3c.dom.Document 對象後,需要通過 DefaultBeanDefinitionDocumentReader 進行解析,解析出 XML 文件中定義的 BeanDefinition 並進行註冊,先來回顧一下上一篇文章中的這段程式碼:

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 並註冊,程式碼如下:

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 並註冊

構造函數

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 並註冊,方法如下:

@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 <beans/>} 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> 檢查 <beans /> 根標籤的命名空間是否為空,或者是 //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 的最頂層的標籤,也就是 <beans />,然後對其子標籤進行解析,這裡的過程大致如下:

  1. 創建 BeanDefinitionParserDelegate 對象 delegate,並初始化默認值
  2. 檢查 <beans /> 根標籤是否是默認命名空間(xmlns 屬性,為空或者是 //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 並註冊,方法如下:

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 version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:context="//www.springframework.org/schema/context"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans
       //www.springframework.org/schema/beans/spring-beans.xsd
       //www.springframework.org/schema/context
       //www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="org.geekbang.thinking.in.spring.ioc.overview" />

    <bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="小馬哥"/>
    </bean>
</beans>

在 XML 文件中的標籤的 xmlns 可以定義默認的命名空間,xmlns:context 定義 context 的命名空間,xsi:schemaLocation 定義了命名空間對應的 XSD 文件(校驗 XML 內容)。

上面的 <beans /><bean> 標籤的命名空間為 //www.springframework.org/schema/beans,其中 <context:component-scan /> 標籤的命名空間為 //www.springframework.org/schema/context(不是默認命名空間)


本文主要分析默認命名空間的解析過程,其他命名空間(註解相關)在後面進行分析,基於 Extensible XML authoring 擴展 Spring XML 元素會進入這裡,主要是通過 NamespaceHandler 這個介面實現的。例如 Spring 集成 Mybatis 項目中的 <mybatis:scan /> 就是擴展 Spring XML 元素,具體實現在後面分析;Spring 的<context:component-scan /> 最終會通過 ComponentScanBeanDefinitionParser 進行解析。ComponentScanBeanDefinitionParser 是將 @Component 註解標註的類轉換成 BeanDefinition 的解析器。

3. parseDefaultElement 方法

parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法,處理默認命名空間的節點,方法如下:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        // 解析 `<import />`
        importBeanDefinitionResource(ele);
    }
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        // 解析 `<alias />`,將 name 對應的 alias 別名進行註冊
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        // 解析 `<bean />`
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // 循環處理,解析 `<beans />`
        doRegisterBeanDefinitions(ele);
    }
}

解析下面四種標籤:

  • <import /> 標籤,例如這麼配置:<import resource="dependency-lookup-context.xml"/>,那麼這裡會獲取到對應的 XML 文件,然後進行相同的處理過程

  • <alias /> 標籤,將 name 對應的 alias 別名進行註冊,往 AliasRegistry 註冊(BeanDefinitionRegistry 繼承了它),也就是說你可以通過別名找到對應的 Bean

  • <bean /> 標籤,解析成 BeanDefinition 並註冊,調用 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法

  • <beans /> 標籤,循環處理,和前面的步驟相同

4. processBeanDefinition 方法

processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法,將 <bean /> 標籤解析成 BeanDefinition 並註冊,方法如下:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // <1> 解析 `<bean />` 標籤,返回 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. 解析 <bean /> 標籤,返回 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,方法如下:

@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<String> 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> 解析 `<bean />` 標籤相關屬性,構造出一個 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. 獲取標籤的 idname 屬性
    2. name 屬性全部添加至別名集合 aliases
    3. 設置 Bean 的名稱 beanName,優先 id 屬性,其次 name 屬性
    4. 檢查 beanName 的唯一性
  2. 解析 <bean /> 標籤相關屬性,構造出一個 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) 方法,解析 <bean /> 成 GenericBeanDefinition 對象,方法如下:

@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> 解析 `<bean />` 的各種屬性並賦值
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        // 提取 description
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

        // <4> 解析 `<bean />` 的子標籤,生成的對象設置到 `bd` 中

        // <4.1> 解析 `<meta />` 元數據標籤
        parseMetaElements(ele, bd);
        // <4.2> 解析 `<lookup-method />` 標籤
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // <4.3> 解析 `<replaced-method />` 標籤
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

        // <4.4> 解析 `<constructor-arg />` 構造函數的參數集合標籤
        parseConstructorArgElements(ele, bd);
        // <4.5> 解析 `<property />` 屬性標籤
        parsePropertyElements(ele, bd);
        // <4.5> 解析 `<qualifier />` 標籤
        parseQualifierElements(ele, bd);

        // <5> 設置 Bean 的 `resource` 資源為 XML 文件資源
        bd.setResource(this.readerContext.getResource());
        // <6> 設置 Bean 的 `source` 來源為 `<bean />` 標籤對象
        bd.setSource(extractSource(ele));

        return bd;
    }
    // ... 省略 catch 各種異常
    finally {
        this.parseState.pop();
    }

    return null;
}

過程如下:

  1. 獲取 classparent 屬性
  2. 構建一個 GenericBeanDefinition 對象 bd,設置 parentNamebeanClass(Class 對象)或者 className(Class 名稱)
  3. 解析 <bean /> 的各種屬性並賦值:scope、abstract、lazy-init、autowire、depends-on、autowire-candidate、primary、init-method、destroy-method、factory-method
  4. 解析 <bean /> 的子標籤,生成的對象設置到 bd
    1. 解析 <meta /> 元數據標籤,將 key-value 保存至 Map 中
    2. 解析 <lookup-method /> 標籤,解析成 LookupOverride 對象,用於實現 Bean 中的某個方法
    3. 解析 <replaced-method /> 標籤,解析成 ReplaceOverride 對象,用於替換 Bean 中的某個方法
    4. 解析 <constructor-arg /> 構造函數的參數集合標籤,將各個參數解析出來,可根據 index 屬性進行排序
    5. 解析 <property /> 屬性標籤,將各個屬性解析出來,每個屬性對應一個 PropertyValue,添加至 bd 的 MutablePropertyValues 屬性中
    6. 解析 <qualifier /> 標籤,解析出需要注入的對象 AutowireCandidateQualifier
  5. 設置 Bean 的 resource 資源為 XML 文件資源
  6. 設置 Bean 的 source 來源為 <bean /> 標籤對象

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,方法如下:

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

// 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<String> 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 別名

// 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 + "'");
         }
      }
   }
}

邏輯不複雜,將 aliasbeanName 映射保存至 aliasMap 中(ConcurrentHashMap)

總結

解析出 XML 文件中的 BeanDefinition 並註冊的整個過程大致如下:

  1. 根據 XSD 文件對 XML 文件進行校驗
  2. 將 XML 文件資源轉換成 org.w3c.dom.Document 對象
  3. 根據 Document 對象解析 <beans /> 標籤,遍歷所有的子標籤
    1. 如果是子標籤是默認的命名空間(為空或者 //www.springframework.org/schema/beans)則進行處理,例如:<import><alias /><bean /><beans />,其中 <bean /> 會被解析出一個 GenericBeanDefinition 對象,然後進行註冊
    2. 否則,找到對應的 NamespaceHandler 對象進行解析,例如:<context:component-scan /><context:annotation-config /><util:list />,這些非默認命名空間的標籤都會有對應的 BeanDefinitionParser 解析器

至此,我們通過 XML 文件定義的 Bean 已經轉換成了 Bean 的「前身」,也就是 BeanDefinition 對象。接下來會分析在 XML 文件中,非默認命名空間的標籤是如何進行處理的。