死磕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 /> 根標簽的命名空間是否為空,或者是 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 文件中,非默認命名空間的標簽是如何進行處理的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM