Spring源码——默认标签解析

本文深入探讨了Spring框架中bean标签的解析与注册过程,详细解析了bean、alias、import和嵌入式beans等标签的处理逻辑。文章通过源码分析,展示了Spring如何将XML配置转化为BeanDefinition,并注册到BeanDefinitionRegistry中,包括bean的属性解析、自定义标签处理以及别名和导入其他配置文件的解析步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。

本文主要涉及书中第三章的部分,依照书中内容以及个人理解对Spring进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework

上一篇文章中,我们已经对Spring读取配置文件注册Bean的流程大致了解了,着重跟随代码的流程一步步查看Spring是如何为DI进行准备的。当然我们最终没有看到它是如何一步步解析XML中的标签并生成我们的依赖对象并进行注入的,那么本文接着来继续学习Spring是如何解析内置标签的。

I. bean标签的解析及注册

默认标签解析是在 parseDefaultElement 中实现的,函数的功能主要是对四种标签(import,alias,bean 和 beans)进行不同的解析。

/**
 * 解析默认标签<import> <alias> <bean> 嵌套的<beans>
 * @param ele 每一个标签
 * @param delegate 翻译为:bean定义解析的代表
 */
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    // 是否是<import>标签
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    // 是否是<alias>标签
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    // 是否是<bean>标签(最为复杂)
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    // 是否是嵌套的<beans>标签
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}

IDEA编写提示

四种标签中,其中 bean 标签最为复杂常见,下面先介绍 bean 标签的解析过程。点进 processBeanDefinition 函数:

/**
 * 解析<bean>标签
 * Process the given bean element, parsing the bean definition
 * and registering it with the registry.
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 委托BeanDefinitionParserDelegate对ele进行解析,bdHolder已经包含配置文件中配置的各种属性,例如class,name,id,alias
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 默认标签下若存在自定义属性,还需要再次对自定义标签进行解析
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance. 解析完成,需要对解析后的btHolder进行注册
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" +
                                     bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event. 最后发出注册响应事件,通知相关的监听器,这个bean已经加载完成
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

传入的 Element ele 相当于一个 <bean>···</bean> 包含的内容。该函数的时序逻辑如下图所示:

bean标签的解析及注册时序图

该函数主要完成四件事:

  • 委托 BeanDefinitionParserDelegateele 进行解析,返回的 BeanDefinitionHolder 类型的 bdHolder 已经包含配置文件中对该 bean 配置的各种属性,例如 class, name, id, alias
  • 如果返回的 bdHolder 不为空,默认 bean 标签下若存在自定义属性,还需要再次对自定义标签进行解析;
  • 解析完成,需要对解析后的btHolder进行注册。同样,注册操作交给了 BeanDefinitionReaderUtilsregisterBeanDefinition 方法去完成;
  • 最后发出注册响应事件,通知相关的监听器,这个bean已经加载完成。

解析BeanDefinition

下面就针对上面的四步骤进行一一详细跟踪。从元素解析及信息提取开始,我们点进 BeanDefinitionParserDelegateparseBeanDefinitionElement 方法:

/**
 * bean标签信息提取
 * Parses the supplied {@code <bean>} element. May return {@code null}
 * if there were errors during parse. Errors are reported to the
 * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
 */
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}

/**
 * bean标签信息提取
 * Parses the supplied {@code <bean>} element. May return {@code null}
 * if there were errors during parse. Errors are reported to the
 * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
 */
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // 1. 获取id和name属性值
    String id = ele.getAttribute(ID_ATTRIBUTE);
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

    // name可能设置了多个,要解析成数组添加至aliases
    List<String> aliases = new ArrayList<>();
    if (StringUtils.hasLength(nameAttr)) {
        // 以','、';'或' '分割
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }

    // 默认设置了id属性,则bean的名称就为id
    // 如果id不存在name属性存在,则bean的名称设置为alias的第一个元素,剩下的作为别名
    String beanName = id;
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
        beanName = aliases.remove(0);
        if (logger.isDebugEnabled()) {
            logger.debug("No XML 'id' specified - using '" + beanName +
                         "' as bean name and " + aliases + " as aliases");
        }
    }

    if (containingBean == null) {
        // 检验bean是否已经被用,没有重复则保存bean的名称与别名
        checkNameUniqueness(beanName, aliases, ele);
    }

    // 2. 进一步解析其他所有属性
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    if (beanDefinition != null) {
        // 3. 如果id和name属性都没有指定,则Spring会自行创建beanName以及指定别名
        if (!StringUtils.hasText(beanName)) {
            try {
                if (containingBean != null) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(
                        beanDefinition, this.readerContext.getRegistry(), true);
                }
                else {
                    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.isDebugEnabled()) {
                    logger.debug("Neither XML 'id' nor 'name' specified - " +
                                 "using generated bean name [" + beanName + "]");
                }
            }
            catch (Exception ex) {
                error(ex.getMessage(), ele);
                return null;
            }
        }
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        // 4. 将解析完成的信息封装至BeanDefinitionHolder实例中
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    return null;
}

通过阅读源码以及注释,可以看到,该函数也主要分成四个步骤:

  1. 在全面解析所有属性前,先提取元素中的 idname 属性;
  2. 进一步解析其他所有属性,并封装信息至 GenericBeanDefinition 类型的实例对象 beanDefinition 中;
  3. 如果检测到 bean 没有指定的 beanName,那么Spring使用默认规则给该 bean 生成 beanName;
  4. 将解析出的所有信息封装到 BeanDefinitionHolder 实例对象中。

第 1 步逻辑简单,而关于 Spring 对 bean 的 name设置和我们平时熟知的规则是一致的。我们进一步查看步骤 2 解析其他属性的函数 parseBeanDefinitionElement(ele, beanName, containingBean)

/**
 * bean标签除了id和name其他属性信息的解析
 * Parse the bean definition itself, without regard to name or aliases. May return
 * {@code null} if problems occurred during the parsing of the bean definition.
 */
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
    Element ele, String beanName, @Nullable BeanDefinition containingBean) {

    this.parseState.push(new BeanEntry(beanName));

    // 解析class属性
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    // 解析parent属性
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
        parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }

    try {
        // 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition对象,创建随后立刻保存class和parent属性
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);

        // 硬编码解析默认bean的各种属性
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

        // 设置描述 内容来自description子元素
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

        // 解析元数据<meta key="" value="">
        parseMetaElements(ele, bd);
        // 解析lookup-method属性
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // 解析replace-method属性
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

        // 解析构造函数参数
        parseConstructorArgElements(ele, bd);
        // 解析property子元素
        parsePropertyElements(ele, bd);
        // 解析qualifier子元素
        parseQualifierElements(ele, bd);

        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));

        return bd;
    }
    catch (ClassNotFoundException ex) {
        error("Bean class [" + className + "] not found", ele, ex);
    }
    catch (NoClassDefFoundError err) {
        error("Class that bean class [" + className + "] depends on not found", ele, err);
    }
    catch (Throwable ex) {
        error("Unexpected failure during bean definition parsing", ele, ex);
    }
    finally {
        this.parseState.pop();
    }

    return null;
}

可以看到,有关 bean 的其他所有属性的解析都在该函数中完成了。开始我们可以看到直接解析 classparent 属性。然后创建了用于承载属性的 AbstractBeanDefinition 类型的 GenericBeanDefinition 对象,创建随后立刻保存 classparent 属性。

① 创建用于承载属性的BeanDefinition

BeanDefinition 本身是一个接口,Spring 中提供了三种相关的实现类,如下图所示。三种实现类均继承自该接口的抽象实现类 AbstractBeanDefinitionBeanDefinition 是配置文件 <bean> 元素标签在容器中的内部表示形式。 <bean>元素标签拥有的classscopelazy-init 等配置属性对应 BeanDefinition 也有相应的 beanClassscopelazyInit 等属性进行一一对应。

BeanDefinition及其实现类

其中,RootBeanDefinition 是最常用的实现类,它对应一般性的元素标签;GenericBeanDefinition 是自2.5版本以后新加入的 bean 文件配置属性定义类,是一站式服务类。在配置文件中用 parent 属性可以定义父 <bean> 和子 <bean> ,父 <bean>RootBeanDefinition表示,而子 <bean>ChildBeanDefiniton 表示,而没有父 <bean><bean> 就使用 RootBeanDefinition 表示。AbstractBeanDefinition 是对两者共同的类信息进行抽象。

Spring 通过 BeanDefinition配置文件中的配置信息转换为容器的内部表示,并将这些 BeanDefinition 注册到 BeanDefinitionRegistry 中。Spring 容器的 BeanDefinitionRegistry 就像是 Spring 配置信息的内存数据库,主要是以 map 的形式保存,后续操作直接从 BeanDefinitionRegistry 中读取配置信息。

因此,要想解析保存 bean 的属性信息,需要先创建 BeanDefinition 的实例。代码中实际调用 createBeanDefinition 方法创建了 GenericBeanDefinition 类型的实例来保存属性信息。

/**基于class和parent创建一个bean definition
 * Create a bean definition for the given class name and parent name.
 * @param className the name of the bean class
 * @param parentName the name of the bean's parent bean
 * @return the newly created bean definition
 * @throws ClassNotFoundException if bean class resolution was attempted but failed
 */
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
    throws ClassNotFoundException {

    return BeanDefinitionReaderUtils.createBeanDefinition(
        parentName, className, this.readerContext.getBeanClassLoader());
}

实际则又委托 BeanDefinitionReaderUtils 去进行创建:

/**
 * Create a new GenericBeanDefinition for the given parent name and class name,
 * eagerly loading the bean class if a ClassLoader has been specified.
 * @param parentName the name of the parent bean, if any
 * @param className the name of the bean class, if any
 * @param classLoader the ClassLoader to use for loading bean classes
 * (can be {@code null} to just register bean classes by name)
 * @return the bean definition
 * @throws ClassNotFoundException if the bean class could not be loaded
 */
public static AbstractBeanDefinition createBeanDefinition(
    @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

    // 创建GenericBeanDefinition实例
    GenericBeanDefinition bd = new GenericBeanDefinition();
    // 设置bd的parentName(bean标签的parent属性可能为空)
    bd.setParentName(parentName);

    if (className != null) {
        if (classLoader != null) {
            // 如果classLoader不为空,则使用以传入的classLoader同一虚拟机加载类对象,否则只是记录className
            bd.setBeanClass(ClassUtils.forName(className, classLoader));
        }
        else {
            bd.setBeanClassName(className);
        }
    }
    return bd;
}
② 进一步解析各种属性

创建完 bean 信息的承载实例后,便可以进行各种 bean 配置属性的解析了。先进入硬编码解析默认bean的各种属性的方法 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd)

/**
 * 硬编码解析默认bean的各种属性,返回保存了配置信息的bd
 */
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
                                                            @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

    // 判断是否含有singleton属性,新版本要用scope属性
    if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
        error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
    } else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
        bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
    } else if (containingBean != null) {
        // Take default from containing bean in case of an inner bean definition.
        bd.setScope(containingBean.getScope());
    }

    // 设置abstract属性
    if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
        bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
    }

    // 设置lazy-init属性,默认default为true
    String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
    if (DEFAULT_VALUE.equals(lazyInit)) {
        lazyInit = this.defaults.getLazyInit();
    }
    bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

    // 设置autowire属性
    String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
    bd.setAutowireMode(getAutowireMode(autowire));

    // 设置depends-on属性
    if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
        String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
        bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
    }

    // 设置autowire-candidate属性
    String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
    if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
        String candidatePattern = this.defaults.getAutowireCandidates();
        if (candidatePattern != null) {
            String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
            bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
        }
    }
    else {
        bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
    }

    // 设置primary属性
    if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
        bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
    }

    // 设置init-method属性
    if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
        String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
        bd.setInitMethodName(initMethodName);
    }
    else if (this.defaults.getInitMethod() != null) {
        bd.setInitMethodName(this.defaults.getInitMethod());
        bd.setEnforceInitMethod(false);
    }

    // 设置destory-method
    if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
        String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
        bd.setDestroyMethodName(destroyMethodName);
    }
    else if (this.defaults.getDestroyMethod() != null) {
        bd.setDestroyMethodName(this.defaults.getDestroyMethod());
        bd.setEnforceDestroyMethod(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值