• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

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

其他 月圆吖 1296次浏览 0个评论

该系列文章是本人在学习 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 文件中,非默认命名空间的标签是如何进行处理的。


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明死磕Spring之IoC篇 – BeanDefinition 的解析阶段(XML 文件)
喜欢 (0)

您必须 登录 才能发表评论!

加载中……