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

月圓吖發表於2021-02-24

該系列文章是本人在學習 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 /> 根標籤的名稱空間是否為空,或者是 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 的最頂層的標籤,也就是 <beans />,然後對其子標籤進行解析,這裡的過程大致如下:

  1. 建立 BeanDefinitionParserDelegate 物件 delegate,並初始化預設值
  2. 檢查 <beans /> 根標籤是否是預設名稱空間(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 並註冊,方法如下:

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="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://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> 標籤的名稱空間為 http://www.springframework.org/schema/beans,其中 <context:component-scan /> 標籤的名稱空間為 http://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. 如果是子標籤是預設的名稱空間(為空或者 http://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 檔案中,非預設名稱空間的標籤是如何進行處理的。

相關文章