日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

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

 Coder編程 2021-04-29

該系列文章是本人在學(xué)習(xí) Spring 的過程中總結(jié)下來的,里面涉及到相關(guān)源碼,可能對讀者不太友好,請結(jié)合我的源碼注釋 Spring 源碼分析 GitHub 地址 進(jìn)行閱讀

Spring 版本:5.1.14.RELEASE

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

該系列其他文章請查看:《死磕 Spring 之 IoC 篇 - 文章導(dǎo)讀》

BeanDefinition 的解析階段(XML 文件)

上一篇文章《BeanDefinition 的加載階段(XML 文件)》獲取到 org.w3c.dom.Document 對象后,需要通過 DefaultBeanDefinitionDocumentReader 進(jìn)行解析,解析出 XML 文件中定義的 BeanDefinition 并進(jìn)行注冊,先來回顧一下上一篇文章中的這段代碼:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // <1> 創(chuàng)建 BeanDefinitionDocumentReader 對象
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    // <2> 獲取已注冊的 BeanDefinition 數(shù)量
    int countBefore = getRegistry().getBeanDefinitionCount();
    // <3> 創(chuàng)建 XmlReaderContext 對象(讀取 Resource 資源的上下文對象)
    // <4> 根據(jù) Document、XmlReaderContext 解析出所有的 BeanDefinition 并注冊
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    // <5> 計(jì)算新注冊的 BeanDefinition 數(shù)量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

本文開始分析第 4 步,BeanDefinition 的解析階段,其中 BeanDefinitionDocumentReader 只有 DefaultBeanDefinitionDocumentReader 一個默認(rèn)實(shí)現(xiàn)類

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 默認(rèn)的 BeanDefinitionDocumentReader 實(shí)現(xiàn)類,從 XML 文件中解析出 BeanDefinition 并注冊

構(gòu)造函數(shù)

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 文件中常用的標(biāo)簽

1. registerBeanDefinitions 方法

registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 方法,根據(jù) Document、XmlReaderContext 解析出所有的 BeanDefinition 并注冊,方法如下:

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    // 獲得 XML Document Root Element
    // 執(zhí)行注冊 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 對象,避免再次調(diào)用當(dāng)前方法時解析出現(xiàn)問題(默認(rèn)值可能不同)
    BeanDefinitionParserDelegate parent = this.delegate;
    // <1> 創(chuàng)建 BeanDefinitionParserDelegate 對象 `delegate`,并初始化默認(rèn)值
    this.delegate = createDelegate(getReaderContext(), root, parent);

    // <2> 檢查 <beans /> 根標(biāo)簽的命名空間是否為空,或者是 http://www./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> 根據(jù) Spring Environment 進(jìn)行校驗(yàn),如果所有 `profile` 都無效,則不進(jìn)行注冊
            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);
    // 設(shè)置 delegate 回老的 BeanDefinitionParserDelegate 對象
    this.delegate = parent;
}

首先獲取 XML Document 的最頂層的標(biāo)簽,也就是 <beans />,然后對其子標(biāo)簽進(jìn)行解析,這里的過程大致如下:

  1. 創(chuàng)建 BeanDefinitionParserDelegate 對象 delegate,并初始化默認(rèn)值
  2. 檢查 <beans /> 根標(biāo)簽是否是默認(rèn)命名空間(xmlns 屬性,為空或者是 http://www./schema/beans),是的話進(jìn)行校驗(yàn)
    1. 獲取 profile 屬性,使用分隔符切分
    2. 根據(jù) Spring Environment 進(jìn)行校驗(yàn),如果所有 profile 都無效,則不進(jìn)行注冊
  3. 解析前處理,空方法,暫時忽略
  4. 解析出 XML Document 中的 BeanDefinition 并注冊,調(diào)用 parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 方法
  5. 解析后處理,空方法,暫時忽略

2. parseBeanDefinitions 方法

parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 方法,解析 XML Document 的最頂層的標(biāo)簽,解析出 BeanDefinition 并注冊,方法如下:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // <1> 如果根節(jié)點(diǎn)使用默認(rèn)命名空間,執(zhí)行默認(rèn)解析
    if (delegate.isDefaultNamespace(root)) {
        // <1.1> 遍歷所有的子節(jié)點(diǎn)
        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> 如果該節(jié)點(diǎn)使用默認(rèn)命名空間,執(zhí)行默認(rèn)解析
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);
                }
                // <1.3> 如果該節(jié)點(diǎn)非默認(rèn)命名空間,執(zhí)行自定義解析
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    // <2> 如果根節(jié)點(diǎn)非默認(rèn)命名空間,執(zhí)行自定義解析
    else {
        delegate.parseCustomElement(root);
    }
}

解析過程大致如下:

  1. 如果根節(jié)點(diǎn)使用默認(rèn)命名空間,執(zhí)行默認(rèn)解析
    1. 遍歷所有的子節(jié)點(diǎn)
    2. 如果該節(jié)點(diǎn)使用默認(rèn)命名空間,執(zhí)行默認(rèn)解析,調(diào)用 parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法
    3. 如果該節(jié)點(diǎn)非默認(rèn)命名空間,執(zhí)行自定義解析,調(diào)用 BeanDefinitionParserDelegate#parseCustomElement(Element ele) 方法
  2. 如果根節(jié)點(diǎn)非默認(rèn)命名空間,執(zhí)行自定義解析,調(diào)用 BeanDefinitionParserDelegate#parseCustomElement(Element ele) 方法

命名空間是什么?

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www./schema/beans"
       xmlns:context="http://www./schema/context"
       xmlns:xsi="http://www./2001/XMLSchema-instance"
       xsi:schemaLocation="http://www./schema/beans
       https://www./schema/beans/spring-beans.xsd
       http://www./schema/context
       https://www./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 文件中的標(biāo)簽的 xmlns 可以定義默認(rèn)的命名空間,xmlns:context 定義 context 的命名空間,xsi:schemaLocation 定義了命名空間對應(yīng)的 XSD 文件(校驗(yàn) XML 內(nèi)容)。

上面的 <beans /><bean> 標(biāo)簽的命名空間為 http://www./schema/beans,其中 <context:component-scan /> 標(biāo)簽的命名空間為 http://www./schema/context(不是默認(rèn)命名空間)


本文主要分析默認(rèn)命名空間的解析過程,其他命名空間(注解相關(guān))在后面進(jìn)行分析,基于 Extensible XML authoring 擴(kuò)展 Spring XML 元素會進(jìn)入這里,主要是通過 NamespaceHandler 這個接口實(shí)現(xiàn)的。例如 Spring 集成 Mybatis 項(xiàng)目中的 <mybatis:scan /> 就是擴(kuò)展 Spring XML 元素,具體實(shí)現(xiàn)在后面分析;Spring 的<context:component-scan /> 最終會通過 ComponentScanBeanDefinitionParser 進(jìn)行解析。ComponentScanBeanDefinitionParser 是將 @Component 注解標(biāo)注的類轉(zhuǎn)換成 BeanDefinition 的解析器。

3. parseDefaultElement 方法

parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法,處理默認(rèn)命名空間的節(jié)點(diǎn),方法如下:

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 對應(yīng)的 alias 別名進(jìn)行注冊
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        // 解析 `<bean />`
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // 循環(huán)處理,解析 `<beans />`
        doRegisterBeanDefinitions(ele);
    }
}

解析下面四種標(biāo)簽:

  • <import /> 標(biāo)簽,例如這么配置:<import resource="dependency-lookup-context.xml"/>,那么這里會獲取到對應(yīng)的 XML 文件,然后進(jìn)行相同的處理過程

  • <alias /> 標(biāo)簽,將 name 對應(yīng)的 alias 別名進(jìn)行注冊,往 AliasRegistry 注冊(BeanDefinitionRegistry 繼承了它),也就是說你可以通過別名找到對應(yīng)的 Bean

  • <bean /> 標(biāo)簽,解析成 BeanDefinition 并注冊,調(diào)用 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法

  • <beans /> 標(biāo)簽,循環(huán)處理,和前面的步驟相同

4. processBeanDefinition 方法

processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法,將 <bean /> 標(biāo)簽解析成 BeanDefinition 并注冊,方法如下:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // <1> 解析 `<bean />` 標(biāo)簽,返回 BeanDefinitionHolder 對象(包含 BeanDefinition、beanName、aliases)
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // <2> 對該標(biāo)簽進(jìn)行裝飾,一般不會,暫時忽略
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance.
            // <3> 進(jìn)行 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> 發(fā)出響應(yīng)事件,通知相關(guān)的監(jiān)聽器,已完成該 Bean 標(biāo)簽的解析
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

過程如下:

  1. 解析 <bean /> 標(biāo)簽,返回 BeanDefinitionHolder 對象(包含 BeanDefinition、beanName、aliases),調(diào)用 BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element ele) 方法
  2. 對該標(biāo)簽進(jìn)行裝飾,和上面處理自定義標(biāo)簽類似,暫時忽略
  3. 進(jìn)行 BeanDefinition 的注冊
  4. 發(fā)出響應(yīng)事件,通知相關(guān)的監(jiān)聽器,已完成該 Bean 標(biāo)簽的解析

BeanDefinitionParserDelegate

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate,解析 XML Document 里面的 BeanDefinition

5. parseBeanDefinitionElement 方法

parseBeanDefinitionElement(Element ele) 方法,將 XML Document 里面的某個標(biāo)簽解析成 BeanDefinition,方法如下:

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // <1> 計(jì)算 BeanDefinition 的 `beanName` 名稱和 `aliases` 別名集合
    // <1.1> 獲取標(biāo)簽的 `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> 設(shè)置 Bean 的名稱,優(yōu)先 `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 />` 標(biāo)簽相關(guān)屬性,構(gòu)造出一個 GenericBeanDefinition 對象
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    if (beanDefinition != null) {
        // <3> 如果不存在 `beanName`,則根據(jù) Class 對象的名稱生成一個
        if (!StringUtils.hasText(beanName)) {
            try {
                if (containingBean != null) { // 內(nèi)部 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> 創(chuàng)建 BeanDefinitionHolder 對象,設(shè)置 `beanName` 名稱和 `aliases` 別名集合,返回
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }

    return null;
}

過程如下:

  1. 計(jì)算 BeanDefinition 的 beanName 名稱和 aliases 別名集合
    1. 獲取標(biāo)簽的 idname 屬性
    2. name 屬性全部添加至別名集合 aliases
    3. 設(shè)置 Bean 的名稱 beanName,優(yōu)先 id 屬性,其次 name 屬性
    4. 檢查 beanName 的唯一性
  2. 解析 <bean /> 標(biāo)簽相關(guān)屬性,構(gòu)造出一個 GenericBeanDefinition 對象,調(diào)用 parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) 方法
  3. 如果不存在 beanName,則根據(jù) Class 對象的名稱生成一個
  4. 創(chuàng)建 BeanDefinitionHolder 對象,設(shè)置 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> 構(gòu)建一個 GenericBeanDefinition 對象 `bd`
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);

        // <3> 解析 `<bean />` 的各種屬性并賦值
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        // 提取 description
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

        // <4> 解析 `<bean />` 的子標(biāo)簽,生成的對象設(shè)置到 `bd` 中

        // <4.1> 解析 `<meta />` 元數(shù)據(jù)標(biāo)簽
        parseMetaElements(ele, bd);
        // <4.2> 解析 `<lookup-method />` 標(biāo)簽
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // <4.3> 解析 `<replaced-method />` 標(biāo)簽
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

        // <4.4> 解析 `<constructor-arg />` 構(gòu)造函數(shù)的參數(shù)集合標(biāo)簽
        parseConstructorArgElements(ele, bd);
        // <4.5> 解析 `<property />` 屬性標(biāo)簽
        parsePropertyElements(ele, bd);
        // <4.5> 解析 `<qualifier />` 標(biāo)簽
        parseQualifierElements(ele, bd);

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

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

    return null;
}

過程如下:

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

lookup-method,會被解析成 LookupOverride 對象,replaced-method 會被解析成 ReplaceOverride 對象,這兩個標(biāo)簽不是很常用

lookup-method:例如一個在一個抽象類中定義了抽象方法,可以通過這個標(biāo)簽定義一個 Bean 實(shí)現(xiàn)這個方法,Spring 會動態(tài)改變抽象類該方法的實(shí)現(xiàn)

replace-method:通過這個標(biāo)簽定義一個 Bean,去覆蓋對應(yīng)的方法,Spring 會動態(tài)替換類的這個方法

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,調(diào)用 BeanDefinitionRegistry#registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法
  2. 注冊 alias 別名,調(diào)用 BeanDefinitionRegistry#registerAlias(String name, String alias) 方法

這里的 BeanDefinitionRegistry 實(shí)現(xiàn)類是 DefaultListableBeanFactory,它是 Spring 底層 IoC 容器,還繼承了 SimpleAliasRegistry(AliasRegistry 實(shí)現(xiàn)類)

8. 注冊 BeanDefinition

// org.springframework.beans.factory.support.DefaultListableBeanFactory
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {

    // 校驗(yàn) beanName 與 beanDefinition 非空
    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    // <1> 校驗(yàn) BeanDefinition
    // 這是注冊前的最后一次校驗(yàn)了,主要是對屬性 methodOverrides 進(jìn)行校驗(yàn)
    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> 如果已經(jīng)存在
    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 {
        // 檢測創(chuàng)建 Bean 階段是否已經(jīng)開啟,如果開啟了則需要對 beanDefinitionMap 進(jìn)行并發(fā)控制
        if (hasBeanCreationStarted()) {
            // beanDefinitionMap 為全局變量,避免并發(fā)情況
            // 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> 重新設(shè)置 beanName 對應(yīng)的緩存
    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

邏輯不復(fù)雜,主要是對 beanName 和該 BeanDefinition 對象的校驗(yàn),最終將其映射保存在 beanDefinitionMap 中(ConcurrentHashMap),key 就是 beanName

9. 注冊 alias 別名

// org.springframework.core.SimpleAliasRegistry
@Override
public void registerAlias(String name, String alias) {
   // 校驗(yàn) 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 ,無需重復(fù)注冊
            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 + "'");
            }
         }
         // 校驗(yàn),是否存在循環(huán)指向
         checkForAliasCircle(name, alias);
         // 注冊 alias
         this.aliasMap.put(alias, name);
         if (logger.isTraceEnabled()) {
            logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
         }
      }
   }
}

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

總結(jié)

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

  1. 根據(jù) XSD 文件對 XML 文件進(jìn)行校驗(yàn)
  2. 將 XML 文件資源轉(zhuǎn)換成 org.w3c.dom.Document 對象
  3. 根據(jù) Document 對象解析 <beans /> 標(biāo)簽,遍歷所有的子標(biāo)簽
    1. 如果是子標(biāo)簽是默認(rèn)的命名空間(為空或者 http://www./schema/beans)則進(jìn)行處理,例如:<import>、<alias />、<bean /><beans />,其中 <bean /> 會被解析出一個 GenericBeanDefinition 對象,然后進(jìn)行注冊
    2. 否則,找到對應(yīng)的 NamespaceHandler 對象進(jìn)行解析,例如:<context:component-scan /> 、<context:annotation-config /><util:list />,這些非默認(rèn)命名空間的標(biāo)簽都會有對應(yīng)的 BeanDefinitionParser 解析器

至此,我們通過 XML 文件定義的 Bean 已經(jīng)轉(zhuǎn)換成了 Bean 的“前身”,也就是 BeanDefinition 對象。接下來會分析在 XML 文件中,非默認(rèn)命名空間的標(biāo)簽是如何進(jìn)行處理的。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多