Spring源码解析(三)——默认标签的解析

Spring源码解析(三)——默认标签的解析

Scroll Down

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);
    }
}

四种标签中,其中 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> 包含的内容。该函数的时序逻辑如下图所示:

image.png

该函数主要完成四件事:

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

解析BeanDefinition

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

//bean标签信息提取
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}
//标签信息提取
@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;
}

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

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

我们进一步查看步骤 2 其他属性的解析过程

//bean标签除了id和name其他属性信息的解析
@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 的其他所有属性的解析都在该函数中完成了。开始我们可以看到直接解析 class 和 parent 属性。然后创建了用于承载属性的 AbstractBeanDefinition 类型的 GenericBeanDefinition 对象,创建随后立刻保存 class 和 parent 属性。

创建用于承载属性的BeanDefinition

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

image.png

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

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

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

//基于class和parent创建一个bean definition
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
    throws ClassNotFoundException {

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

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

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(false);
    }

    // 设置factory-method
    if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
        bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
    }

    // 设置factory-bean
    if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
        bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
    }

    return bd;
}

可以很清晰的看到,Spring 对各种 bean 属性的解析,并将解析的属性信息都 set 至 GenericBeanDefinition 对象中进行返回。这样对于 标签的各种属性全部解析完毕,下面需要处理 标签的子元素。

解析子元素description

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


@Nullable
public static String getChildElementValueByTagName(Element ele, String childEleName) {
    Element child = getChildElementByTagName(ele, childEleName);
    return (child != null ? getTextValue(child) : null);
}

解析子元素meta

属性的使用如下:
<bean id="myTestBean" class="guo.ping.ioc.bean.MyTestBean">
    <meta key="testStr" value="test meta" />
</bean>

这段配置并不会出现在 myTestBean 的属性之中,只是一个额外的声明。Spring 将其解析保存至 BeanDefinitionattribute 中,所以可以利用 getAttribute(key) 来获取。解析方法如下:

/**
 * 解析子元素meta
 * @param ele
 * @param attributeAccessor
 */
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
    // 获取当前节点的所有子元素
    NodeList nl = ele.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        // 提取meta
        if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
            Element metaElement = (Element) node;
            String key = metaElement.getAttribute(KEY_ATTRIBUTE); // 提取key
            String value = metaElement.getAttribute(VALUE_ATTRIBUTE); // 提取value
            // 将每个meta数据封装为BeanMetadataAttribute对象
            BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
            attribute.setSource(extractSource(metaElement));
            // 保存至bd
            attributeAccessor.addMetadataAttribute(attribute);
        }
    }
}

解析子元素constructor-arg

Xml文件构造函数配置:

<beans>
<!-- 默认的情况况下是按照参数的顺序注入,当指定index 索引后就可以改变注入参数的顺序--〉
<bean id=" helloBean " class= "com.HelloBean"〉
<constructor-arg  index="0" 〉
<value >Ajar</ value>
</constructor-arg>
<constructor-arg index="1" >
<value >jiahai </value>
</constructor-arg>
</bean>
</beans>

对HelloBean 自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去。

对于constructor-arg 子元素的解析,Spring 是通过parseConstructorArgElements函数来实现的

/**
 * 解析子元素constructor-arg
 */
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
    NodeList nl = beanEle.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
            // 处理子元素constructor-arg
            parseConstructorArgElement((Element) node, bd);
        }
    }
}

在配置文件中 元素标签内可能包含多个 <constructor-arg> 子元素,所以遍历全部子元素,当是 <constructor-arg> 时,调用 parseConstructorArgElement((Element) node, bd) 方法对每一个元素进行解析。

/**
 * 解析子元素constructor-arg
 */
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
    String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); // index属性
    String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); // type属性
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // name属性
    if (StringUtils.hasLength(indexAttr)) {
        try {
            int index = Integer.parseInt(indexAttr);
            if (index < 0) {
                error("'index' cannot be lower than 0", ele);
            }
            else {
                try {
                    this.parseState.push(new ConstructorArgumentEntry(index));
                    // 解析ele也就是constructor-arg对应的属性元素
                    Object value = parsePropertyValue(ele, bd, null);
                    // 创建ValueHolder对象保存constructor-arg信息
                    ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                    if (StringUtils.hasLength(typeAttr)) {
                        valueHolder.setType(typeAttr);
                    }
                    if (StringUtils.hasLength(nameAttr)) {
                        valueHolder.setName(nameAttr);
                    }
                    valueHolder.setSource(extractSource(ele));
                    if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
                        error("Ambiguous constructor-arg entries for index " + index, ele);
                    }
                    else {
                        // 保存信息至bd的constructorArgumentValues
                        bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
                    }
                }
                finally {
                    this.parseState.pop();
                }
            }
        }
        catch (NumberFormatException ex) {
            error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
        }
    }
    else {
        // 没有index属性则忽略,自动寻找
        try {
            this.parseState.push(new ConstructorArgumentEntry());
            Object value = parsePropertyValue(ele, bd, null);
            ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
            if (StringUtils.hasLength(typeAttr)) {
                valueHolder.setType(typeAttr);
            }
            if (StringUtils.hasLength(nameAttr)) {
                valueHolder.setName(nameAttr);
            }
            valueHolder.setSource(extractSource(ele));
            bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
        }
        finally {
            this.parseState.pop();
        }
    }
}

首先,提取 <constructor-arg> 标签的必要属性 index、type 和 name

然后利用 parsePropertyValue(ele, bd, null) 方法解析 <constructor-arg> 子元素,再用 ConstructorArgumentValues.ValueHolder 类型对象封装解析出的信息。

如果配置了 index 属性,则会将 valueHolder 通过 bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder) 方法保存至 indexedArgumentValues 属性中

而如果没有 index 属性,则会通过 bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder) 保存至 genericArgumentValues 属性中。

是否拥有 index 属性,决定了解析出的信息保存在 BeanDefinition 的哪个属性中。

继续查看其中 Spring 如何解析出 <constructor-arg> 所有信息的。

/**
 * 解析ele对应的属性元素
 */
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
    // 如果propertyName不为空则是解析<property>,否则是<constructor-arg>
    String elementName = (propertyName != null ?
                          "<property> element for property '" + propertyName + "'" :
                          "<constructor-arg> element");

    // 一个属性只能对应一种类型:ref, value, list等
    NodeList nl = ele.getChildNodes();
    Element subElement = null;
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        // 对应description或者meta不包含在内
        if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
            !nodeNameEquals(node, META_ELEMENT)) {
            // Child element is what we're looking for.
            if (subElement != null) {
                error(elementName + " must not contain more than one sub-element", ele);
            }
            else {
                subElement = (Element) node;
            }
        }
    }

    // 解析ref和value属性
    boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
    boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);

    // 不能出现以下两种情形:
    //    1. 同时存在ref和value属性
    //    2. 存在ref属性或者value属性二者之一的同时还存在子元素
    // 这样可以保证只会解析三种情况之一,其他情形都会报错
    if ((hasRefAttribute && hasValueAttribute) ||
        ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
        error(elementName +
              " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
    }

    if (hasRefAttribute) {
        // 先对ref进行判空
        String refName = ele.getAttribute(REF_ATTRIBUTE);
        if (!StringUtils.hasText(refName)) {
            error(elementName + " contains empty 'ref' attribute", ele);
        }
        // 用RuntimeBeanReference对象包装ref属性值
        RuntimeBeanReference ref = new RuntimeBeanReference(refName);
        ref.setSource(extractSource(ele));
        return ref;
    }
    else if (hasValueAttribute) {
        // 用TypedStringValue对象包装value属性值
        TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
        valueHolder.setSource(extractSource(ele));
        return valueHolder;
    }
    else if (subElement != null) {
        // 解析子元素
        return parsePropertySubElement(subElement, bd);
    }
    else {
        // 三者都没有则会报错
        error(elementName + " must specify a ref or value", ele);
        return null;
    }
}

代码内容上,对构造函数中属性元素的解析,经历了以下过程:

  • 略过 description 或者 meta
  • 提取解析 refvalue 属性;
  • RuntimeBeanReference 对象包装 ref 属性值,或者TypedStringValue 对象包装 value 属性值或者解析子元素。

而对于子元素的处理,例如在构造函数中又嵌入了子元素 map 。

<constructor-arg>
    <map>
        <entry key="key" value="value" />
    </map>
</constructor-arg>

Spring 对于 <constructor-arg> 标签的子元素单独封装了 parsePropertySubElement(subElement, bd) 方法对各种子元素的分类处理:

/**
 * Parse a value, ref or collection sub-element of a property or
 * constructor-arg element.
 * @param ele subelement of property element; we don't know which yet
 * @param defaultValueType the default type (class name) for any
 * {@code <value>} tag that might be created
 */
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
    if (!isDefaultNamespace(ele)) {
        return parseNestedCustomElement(ele, bd);
    }
    // 处理嵌入<bean>结点情况
    else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
        BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
        if (nestedBd != null) {
            nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
        }
        return nestedBd;
    }
    // 处理嵌入<ref>结点情况
    else if (nodeNameEquals(ele, REF_ELEMENT)) {
        // A generic reference to any name of any bean.
        String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
        boolean toParent = false;
        if (!StringUtils.hasLength(refName)) {
            // 解析parent
            refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
            toParent = true;
            if (!StringUtils.hasLength(refName)) {
                error("'bean' or 'parent' is required for <ref> element", ele);
                return null;
            }
        }
        if (!StringUtils.hasText(refName)) {
            error("<ref> element contains empty target attribute", ele);
            return null;
        }
        RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
        ref.setSource(extractSource(ele));
        return ref;
    }
    // 处理嵌入<idref>结点情况
    else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
        return parseIdRefElement(ele);
    }
    // 处理嵌入<value>结点情况
    else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
        return parseValueElement(ele, defaultValueType);
    }
    // 处理嵌入<null>结点情况
    else if (nodeNameEquals(ele, NULL_ELEMENT)) {
        // It's a distinguished null value. Let's wrap it in a TypedStringValue
        // object in order to preserve the source location.
        TypedStringValue nullHolder = new TypedStringValue(null);
        nullHolder.setSource(extractSource(ele));
        return nullHolder;
    }
    // 处理嵌入<array>结点情况
    else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
        return parseArrayElement(ele, bd);
    }
    // 处理嵌入<list>结点情况
    else if (nodeNameEquals(ele, LIST_ELEMENT)) {
        return parseListElement(ele, bd);
    }
    // 处理嵌入<set>结点情况
    else if (nodeNameEquals(ele, SET_ELEMENT)) {
        return parseSetElement(ele, bd);
    }
    // 处理嵌入<map>结点情况
    else if (nodeNameEquals(ele, MAP_ELEMENT)) {
        return parseMapElement(ele, bd);
    }
    // 处理嵌入<props>结点情况
    else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
        return parsePropsElement(ele);
    }
    else {
        error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
        return null;
    }
}

可以看到,在上面的函数中实现了所有可以支持的子类的分类处理,到这里,我们已经大致理清构造函数的解析流程。

解析子元素property

parsePropertyElements 函数完成子元素 <property> 的解析, <property> 的使用方式如下:

<bean id="myTestBean" class="guo.ping.ioc.bean.MyTestBean">
    <property name="testStr" value="haha" />
</bean>

或者

<bean id="myTestBean">
    <property name="testStr">
        <list>
            <value>aa</value>
            <value>bb</value>
        </list>
    </property>
</bean>

具体的解析过程如下:

/**
 *  解析子元素property
 * Parse property sub-elements of the given bean element.
 */
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
    NodeList nl = beanEle.getChildNodes();
    // 提取所有的property子元素 调用parsePropertyElement处理
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
            parsePropertyElement((Element) node, bd);
        }
    }
}

提取所有的 property 子元素调用 parsePropertyElement((Element) node, bd) 处理:

/**
 * Parse a property element.
 */
public void parsePropertyElement(Element ele, BeanDefinition bd) {
    // 获取property元素name属性值
    String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
    if (!StringUtils.hasLength(propertyName)) {
        error("Tag 'property' must have a 'name' attribute", ele);
        return;
    }
    this.parseState.push(new PropertyEntry(propertyName));
    try {
        // 不允许多次对同一属性配置
        if (bd.getPropertyValues().contains(propertyName)) {
            error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
            return;
        }
        Object val = parsePropertyValue(ele, bd, propertyName);
        PropertyValue pv = new PropertyValue(propertyName, val);
        parseMetaElements(ele, pv);
        pv.setSource(extractSource(ele));
        bd.getPropertyValues().addPropertyValue(pv);
    }
    finally {
        this.parseState.pop();
    }
}

先获取 <property> 标签的 name,然后同样通过 parsePropertyValue(ele, bd, propertyName) 方法对其它属性进行解析,该方法在解析 <constructor-arg> 时也曾经用过,并且方法通过调用时是否传入 propertyName 参数决定解析的是 <property> 标签还是 <constructor-arg> 标签。最后通过 PropertyValue 进行封装,并记录在 BeanDefinition 中的 propertyValues 属性中。

解析子元素qualifier

对于 <qualifier> 标签,其相关使用这里再给出一篇文章。在Spring自动注入的时候,Spring容器匹配的候选 Bean 数目必须有且只有一个。当找不到一个匹配的 Bean 时,Spring容器会抛出 BeanCreationException异常,并指出必须至少拥有一个匹配的 Bean。

<qualifier> 子元素的使用如下:

<bean id="myTestBean" class="guo.ping.ioc.bean.MyTestBean">
    <qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="qf" />
</bean>

或者:

<qualifier type="org.springframework.beans.factory.annotation.Qualifier">
    <attribute key="key1" value="value1"/>
    <attribute key="key2" value="value2"/>
</qualifier>

对于该标签的解析,Spring利用 parseQualifierElements 方法进行解析。

/**
 * Parse qualifier sub-elements of the given bean element.
 */
public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
    NodeList nl = beanEle.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
            parseQualifierElement((Element) node, bd);
        }
    }
}

同样,提取所有的 qualifier 子元素,调用 parseQualifierElement((Element) node, bd) 处理所有的标签属性。代码中分别对上述两种格式的 <qualifier> 进行解析。

/**
 * Parse a qualifier element.
 */
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
    // 获取type
    String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
    if (!StringUtils.hasLength(typeName)) {
        error("Tag 'qualifier' must have a 'type' attribute", ele);
        return;
    }
    this.parseState.push(new QualifierEntry(typeName));
    try {
        // 利用AutowireCandidateQualifier包装typeName
        AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
        qualifier.setSource(extractSource(ele));
        // 获取value
        String value = ele.getAttribute(VALUE_ATTRIBUTE);
        if (StringUtils.hasLength(value)) {
            qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
        }
        NodeList nl = ele.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
                Element attributeEle = (Element) node;
                String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
                String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
                if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
                    BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
                    attribute.setSource(extractSource(attributeEle));
                    qualifier.addMetadataAttribute(attribute);
                }
                else {
                    error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
                    return;
                }
            }
        }
        bd.addQualifier(qualifier);
    }
    finally {
        this.parseState.pop();
    }
}

AbstractBeanDefinition属性开始之前,如果对于流程不清晰,抑或有点晕,多看看文章的目录结构,理清流程。

现在到这已经完成了对 XML 文档到 GenericBeanDefinition 的转换,也就是说,XML中所有的默认标签相关配置都在 GenericBeanDefinition 的实例类中找到对应的配置。

查看 GenericBeanDefinition 的源码:

public class GenericBeanDefinition extends AbstractBeanDefinition {

    @Nullable
    private String parentName;

    public GenericBeanDefinition() {
        super();
    }

    public GenericBeanDefinition(BeanDefinition original) {
        super(original);
    }

    @Override
    public void setParentName(@Nullable String parentName) {
        this.parentName = parentName;
    }

    @Override
    @Nullable
    public String getParentName() {
        return this.parentName;
    }

    @Override
    public AbstractBeanDefinition cloneBeanDefinition() {
        return new GenericBeanDefinition(this);
    }

    @Override
    public boolean equals(Object other) {
        return (this == other || (other instanceof GenericBeanDefinition && super.equals(other)));
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("Generic bean");
        if (this.parentName != null) {
            sb.append(" with parent '").append(this.parentName).append("'");
        }
        sb.append(": ").append(super.toString());
        return sb.toString();
    }

}

GenericBeanDefinition 中没有我们想的那些对应属性,但其继承自 AbstractBeanDefinition,相关属性应该都在父类中定义了,继续查看。

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
        implements BeanDefinition, Cloneable {
    // 省略静态变量和 final 常量

    /**
     * bean的作用范围,对应bean的属性scope
     */
    @Nullable
    private String scope = SCOPE_DEFAULT;

    /**
     * 是否抽象,对应bean的属性abstract
     */
    private boolean abstractFlag = false;

    /**
     * 是否延迟加载,对应bean的属性lazy-init
     */
    private boolean lazyInit = false;

    /**
     * 自动注入模式,对应bean的属性autowire
     */
    private int autowireMode = AUTOWIRE_NO;

    /**
     * 依赖检查,Spring3.0后弃用这个属性
     */
    private int dependencyCheck = DEPENDENCY_CHECK_NONE;

    /**
     * 用来表示一个bean的实例化依赖另一个bean先实例化,对应bean的属性depend-on
     */
    @Nullable
    private String[] dependsOn;

    /**
     * autowire-candidate属性设置为false,这样容器在查找自动装配对象时,将不考虑该bean,
     * 即它不会被考虑作为其他bean自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其他bean的
     */
    private boolean autowireCandidate = true;

    /**
     * 自动装配时当出现多个bean候选者时,将作为首选者,对应bean属性primary
     */
    private boolean primary = false;

    /**
     * 用于记录Qualifier,对应子元素qualifier
     */
    private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>(0);

    @Nullable
    private Supplier<?> instanceSupplier;

    /**
     * 允许访问非公开的构造器和方法,程序设置
     */
    private boolean nonPublicAccessAllowed = true;

    /**
     * 是否以一种宽松的模式解析构造函数,默认为true。
     * 如果为false,则在如下情况
     * interface ITest{}
     * class ITestImpl implements ITest{};
     * class Main{
     *     Main(ITest i){}
     *     Main(ITestImpl i){}
     * }
     * 抛出异常,因为spring无法准确定位哪个构造函数
     * 程序设置
     */
    private boolean lenientConstructorResolution = true;

    /**
     * 对应bean的属性factory-bean,用法:
     * <bean id="" factory-bean="" factory-method="" />
     */
    @Nullable
    private String factoryBeanName;

    /**
     * 对应bean的属性factory-method,用法:
     * <bean id="" factory-bean="" factory-method="" />
     */
    @Nullable
    private String factoryMethodName;

    /**
     * 记录构造函数注入属性,对应bean属性constructor-arg
     */
    @Nullable
    private ConstructorArgumentValues constructorArgumentValues;

    /**
     * 普通属性集合
     */
    @Nullable
    private MutablePropertyValues propertyValues;

    /**
     * 方法重写的持有者,记录lookup-method、replaced-method元素
     */
    @Nullable
    private MethodOverrides methodOverrides;

    /**
     * 初始化方法,对应bean属性init-method
     */
    @Nullable
    private String initMethodName;

    /**
     * 销毁方法,对应bean属性destroy-method
     */
    @Nullable
    private String destroyMethodName;

    /**
     * 是否执行init-method,程序设置
     */
    private boolean enforceInitMethod = true;

    /**
     * 是否执行destroy-method,程序设置
     */
    private boolean enforceDestroyMethod = true;

    /**
     * 是否是用户自定义而不是程序本身定义的,创建AOP时候为true,程序设置
     */
    private boolean synthetic = false;

    /**
     * 定义这个bean的应用,APPLICATION:用户,INFRASTRUCTURE:完全内部使用,与用户无关,SUPPORT:某些复杂配置的一部分
     * 程序设置
     */
    private int role = BeanDefinition.ROLE_APPLICATION;

    /**
     * bean的描述信息
     */
    @Nullable
    private String description;

    /**
     * 该bean定义的资源
     */
    @Nullable
    private Resource resource;

    ...
}

解析默认标签中的自定义标签元素
我们回顾一下,第一小节 bean 标签的解析和注册的四个步骤代码:

/**
 * 解析<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));
    }
}

之前花了很重的笔墨在说第一步:delegate.parseBeanDefinitionElement(ele) 。接下来第二步 Spring 还要继续对配置文件默认标签的自定义属性进行解析:delegate.decorateBeanDefinitionIfRequired(ele, bdHolder) 。这句代码主要在出现自定义的子元素或者属性时起作用,<myBean> 就是自定义子元素,username 是自定义属性。Spring的 <bean> 标签是默认标签,但其子元素存在自定义情况,此时该函数便起作用了。

<bean id="test" class="test.MyClass">
    <myBean:user username="aaa" />
</bean>

查看 decorateBeanDefinitionIfRequired(ele, bdHolder) 源码:

/**
 * 解析bean标签的自定义属性
 * @param ele bean标签元素
 * @param definitionHolder 保存属性信息的BeanDefinition
 * @return
 */
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
    return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
}

其中又调用了一个重载方法,前两个参数相同,第三个参数为 null。方法主要对所有属性和子节点进行遍历,解析其中的自定义部分。

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
    Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {

    BeanDefinitionHolder finalDefinition = definitionHolder;

    // 遍历所有属性,解析默认标签下的自定义属性
    NamedNodeMap attributes = ele.getAttributes();
    for (int i = 0; i < attributes.getLength(); i++) {
        Node node = attributes.item(i);
        finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
    }

    // 遍历所有子节点,解析默认标签下的自定义子元素
    NodeList children = ele.getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
        Node node = children.item(i);
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
        }
    }
    return finalDefinition;
}

containingBd 参数被传给了 decorateIfRequired()方法,想要知道该参数作用,需要继续查看:

/**
 * 解析bean标签的自定义属性
 * @param node 自定义属性或者子元素的节点
 * @param originalDef 已经包含解析完默认标签默认属性子元素信息的BeanDefinitionHolder对象
 * @param containingBd 父类beanDefinition
 * @return
 */
public BeanDefinitionHolder decorateIfRequired(
    Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

    // 获取自定义标签的命名空间
    String namespaceUri = getNamespaceURI(node);
    // 如果属于自定义标签则进行decorate
    if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
        // 根据命名空间寻找对应的处理器handler
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler != null) {
            // 利用处理器进行修饰
            BeanDefinitionHolder decorated =
                handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
            if (decorated != null) {
                return decorated;
            }
        }
        else if (namespaceUri.startsWith("http://www.springframework.org/")) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
        }
        else {
            // A custom namespace, not to be handled by Spring - maybe "xml:...".
            if (logger.isDebugEnabled()) {
                logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
            }
        }
    }
    return originalDef;
}

该方法逻辑顺序首先获取属性或者元素的命名空间,以此判断是否需要自定义解析。然后根据命名空间寻找对应的处理器 NamespaceHandler,并委托 NamespaceHandler 对象进行解析。关于解析的细节在解析自定义标签时再具体细说,这里说的自定义标签是指和默认标签一个级别的自定义标签。

所以第二步,主要就是解析默认标签的自定义属性或者子元素。

注册解析的BeanDefinition

对于配置文件,解析完了(第一步),装饰结束(第二步),这样基本的 XML 文件信息都保存在 BeanDefinitionHolder 中了,接下来就需要进行注册。Spring 委托 BeanDefinitionReaderUtils 类中的registerBeanDefinition(bdHolder, getReaderContext().getRegistry()) 方法对解析后的 btHolder 进行注册。

/**
 * 解析<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));
    }
}

我们具体查看其中的方法:

/**
 * 对definitionHolder进行注册
 * Register the given bean definition with the given bean factory.
 * @param definitionHolder the bean definition including name and aliases
 * @param registry the bean factory to register with
 * @throws BeanDefinitionStoreException if registration failed
 */
public static void registerBeanDefinition(
    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    throws BeanDefinitionStoreException {

    // 使用beanName做唯一标识注册
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // 注册所有的别名
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

通过上面代码可以清晰的看出,definitionHolder 通过 getBeanDefinition() 提取的 beanDefinition 都根据 beanName 做唯一标识注册到 BeanDefinitionRegistry 类型的实例 registry 中。另外,还要通过 getAliases() 方法取出所有别名,注册别名到 registry 中。

① 通过beanName注册BeanDefinition

我们先来研究注册 BeanDefinition 的第一阶段。对于 beanDefinition 的注册,我们查看这个 registerBeanDefinition 方法的实现类 DefaultListableBeanFactory 中的实现:

/**
 * 通过beanName注册BeanDefinition
 * @param beanName the name of the bean instance to register
 * @param beanDefinition definition of the bean instance to register
 * @throws BeanDefinitionStoreException
 */
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            // 注册前的最后一次校验,这里的校验不同于之前的XML文件校验
            // 主要是对于AbstractBeanDefinition属性中的methodOverrides校验
            // 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                   "Validation of bean definition failed", ex);
        }
    }

    // 从beanDefinitionMap获取beanName的beanDefinition
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    // 如果存在
    if (existingDefinition != null) {
        // 如果beanName对应的beanDefinition不允许被覆盖,则抛出异常
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        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 + "]");
            }
        }
        else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Overriding bean definition for bean '" + beanName +
                             "' with a different definition: replacing [" + existingDefinition +
                             "] with [" + beanDefinition + "]");
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("Overriding bean definition for bean '" + beanName +
                             "' with an equivalent definition: replacing [" + existingDefinition +
                             "] with [" + beanDefinition + "]");
            }
        }
        // 更新beanName的beanDefinition
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    // 如果不存在
    else {
        // 工厂的bean创建阶段是否已经开始
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            // 因为beanDefinitionMap是全局变量,这里肯定会存在并发访问的情况
            synchronized (this.beanDefinitionMap) {
                // beanDefinitionMap中添加beanDefinition
                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;
                // 如果手动注册的单例的名称的list集合中包含beanName,就移除
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
        }
        // 仍然处于启动注册阶段
        else {
            // 注册beanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 记录beanName
            this.beanDefinitionNames.add(beanName);
            // 手动注册的单例的名称的list集合移除beanName
            this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {
        // 重置所有beanName对应的缓存
        resetBeanDefinition(beanName);
    }
}

上面的代码经过较为详细的注释,在对 bean 的注册处理上,主要经历以下步骤:

对于 AbstractBeanDefinition 的校验。主要是对于 AbstractBeanDefinition 属性中的 methodOverrides 校验,校验 methodOverrides 是否与工厂方法并存或者 methodOverrides 对应的方法根本不存在;
beanName 已经注册的情况处理。如果 beanName 对应的 beanDefinition 不允许被覆盖,则抛出异常;
beanName 和对应的 beanDefinition 加入到 bean 定义对象的 Map 集合 beanDefinitionMap 中;
清除解析之前留下的对应 beanName 的缓存。

② 通过别名注册BeanDefinition

注册别名类似注册 beanName

/**
 * 注册所有的别名
 * @param name the canonical name
 * @param alias the alias to be registered
 */
@Override
public void registerAlias(String name, String alias) {
    Assert.hasText(name, "'name' must not be empty");
    Assert.hasText(alias, "'alias' must not be empty");
    synchronized (this.aliasMap) {
        // 如果beanName与alias相同的话不记录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 {
            String registeredName = this.aliasMap.get(alias);
            if (registeredName != null) {
                // 已经存在就不需要注册了
                if (registeredName.equals(name)) {
                    // An existing alias - no need to re-register
                    return;
                }
                // 如果alias不允许被覆盖则抛出异常
                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 + "'");
                }
            }
            // alias循环检查 当A->B存在时,若再次出现A->C->B时候会抛出异常
            checkForAliasCircle(name, alias);
            this.aliasMap.put(alias, name);
            if (logger.isTraceEnabled()) {
                logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
            }
        }
    }
}

同样,对于 alias 的注册,经历以下步骤:

aliasbeanName 相同情况处理。若 aliasbeanName 名称相同则不需要处理并删除掉原有 alias
alias 覆盖处理。若 aliasName已经使用并已经指向了另一 beanName 则返回;
alias 循环检查。当 A->B 存在时,若再次出现 A->C>B 时候则会抛出异常;
注册 alias

我们看一下这个循环检查如何实现的:

/**
 * Check whether the given name points back to the given alias as an alias
 * in the other direction already, catching a circular reference upfront
 * and throwing a corresponding IllegalStateException.
 */
protected void checkForAliasCircle(String name, String alias) {
    if (hasAlias(alias, name)) {
        throw new IllegalStateException("Cannot register alias '" + alias +
                                        "' for name '" + name + "': Circular reference - '" +
                                        name + "' is a direct or indirect alias for '" + alias + "' already");
    }
}
/**
 * Determine whether the given name has the given alias registered.
 */
public boolean hasAlias(String name, String alias) {
    for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
        String registeredName = entry.getValue();
        if (registeredName.equals(name)) {
            String registeredAlias = entry.getKey();
            return (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias));
        }
    }
    return false;
}

通知监听器解析及注册完成

/**
 * 解析<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));
    }
}

完成注册,此时已经进入第四步:发出注册响应事件,通知相关的监听器,这个bean已经加载完成。当程序需要对注册 BeanDefinition 进行监听时可以通过注册监听器的方式并将逻辑写入监听器中,目前Spring中并未对此事件做任何逻辑处理。

③. alias标签的解析

第一大部分我们终于研究完了关于 <bean> 标签的解析,接下来回过头研究 <alias> 标签的解析过程。

/**
 * 解析默认标签<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);
    }
}

在对 bean 进行定义时,除了使用 id 属性来指定名称外,还可以使用 alias 标签来指定其他的别名。这些所有的名称都指向同一个 bean,在某些情况下提供别名十分有用,比如让应用的每一个组件能更容易的对公共组件进行引用。如下述 XML 配置示例,dataSourcedataadatab 是同一bean的名称。不同的组件,可以各自通过一个名字对同一对象进行引用,比如 A 组件用dataa,B 组件用datab`

<alias name="dataSource" alias="dataa"/>  
<alias name="dataSource" alias="datab"/>  

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
      destroy-method="close">  
    <property name="driverClassName" value="${driver}" />  
    <property name="url" value="${url}" />  
    <property name="username" value="${username}" />  
    <property name="password" value="${password}" />  
</bean>

<alias> 标签通过 DefaultBeanDefinitionDocumentReader 类的 processAliasRegistration(ele) 方法进行解析。

/**
 * 解析注册默认标签<alias></alias>
 * Process the given alias element, registering the alias with the registry.
 */
protected void processAliasRegistration(Element ele) {
    // 获取name和alias属性
    String name = ele.getAttribute(NAME_ATTRIBUTE);
    String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
    boolean valid = true;
    if (!StringUtils.hasText(name)) {
        getReaderContext().error("Name must not be empty", ele);
        valid = false;
    }
    if (!StringUtils.hasText(alias)) {
        getReaderContext().error("Alias must not be empty", ele);
        valid = false;
    }
    if (valid) {
        try {
            // 注册别名
            getReaderContext().getRegistry().registerAlias(name, alias);
        }
        catch (Exception ex) {
            getReaderContext().error("Failed to register alias '" + alias +
                                     "' for bean with name '" + name + "'", ele, ex);
        }
        // 别名注册完成后通知监听器做相应处理
        getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
    }
}

分析完 <bean> 标签的解析注册,相信对这段代码很容易解释了。至于注册别名的过程,代码见下方,没错,和之前 <bean> 标签的解析注册中的注册别名过程调用的是同一函数,都是 SimpleAliasRegistry 类中的 registerAlias 方法。

/**
 * 注册所有的别名
 * @param name the canonical name
 * @param alias the alias to be registered
 */
@Override
public void registerAlias(String name, String alias) {
    Assert.hasText(name, "'name' must not be empty");
    Assert.hasText(alias, "'alias' must not be empty");
    synchronized (this.aliasMap) {
        // 如果beanName与alias相同的话不记录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 {
            String registeredName = this.aliasMap.get(alias);
            if (registeredName != null) {
                // 已经存在就不需要注册了
                if (registeredName.equals(name)) {
                    // An existing alias - no need to re-register
                    return;
                }
                // 如果alias不允许被覆盖则抛出异常
                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 + "'");
                }
            }
            // alias循环检查 当A->B存在时,若再次出现A->C->B时候会抛出异常
            checkForAliasCircle(name, alias);
            this.aliasMap.put(alias, name);
            if (logger.isTraceEnabled()) {
                logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
            }
        }
    }
}
④. import标签的解析

已经完成对 <bean><alias> 标签的解析,接下来看看 <import> 标签。<import> 标签一般用于多个 Spring 配置文件的整合 ,比如:

<import resource="classpath:aaa.xml"/>

Spring 解析 <import> 标签利用 importBeanDefinitionResource(ele) 方法:

/**
 * 解析注册默认标签<import></import>
 * Parse an "import" element and load the bean definitions
 * from the given resource into the bean factory.
 */
protected void importBeanDefinitionResource(Element ele) {
    // 获取resource属性
    String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
    if (!StringUtils.hasText(location)) {
        getReaderContext().error("Resource location must not be empty", ele);
        return;
    }

    // 解析系统属性,格式如:"${user.dir}"
    location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

    Set<Resource> actualResources = new LinkedHashSet<>(4);

    // 绝对URI还是相对URI
    boolean absoluteLocation = false;
    try {
        absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
    }
    catch (URISyntaxException ex) {
        // cannot convert to an URI, considering the location relative
        // unless it is the well-known Spring prefix "classpath*:"
    }

    // 如果是绝对URI则直接根据地址进行加载对应的配置文件
    if (absoluteLocation) {
        try {
            int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
            if (logger.isDebugEnabled()) {
                logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
            }
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error(
                "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
        }
    }
    // 如果是相对URI则根据相对地址计算出绝对地址
    else {
        // No URL -> considering resource location as relative to the current file.
        try {
            int importCount;
            // Resource存在多个子实现类,如VfsResource、FileSystemResource等,
            // 每个resource的createRelative方式实现都不一样,所以这里先使用子类的方法进行尝试解析
            Resource relativeResource = getReaderContext().getResource().createRelative(location);
            if (relativeResource.exists()) {
                importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                actualResources.add(relativeResource);
            }
            // 如果解析不成功,则使用默认的解析器ResourcePatternSolver进行解析
            else {
                String baseLocation = getReaderContext().getResource().getURL().toString();
                importCount = getReaderContext().getReader().loadBeanDefinitions(
                    StringUtils.applyRelativePath(baseLocation, location), actualResources);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
            }
        }
        catch (IOException ex) {
            getReaderContext().error("Failed to resolve current resource location", ele, ex);
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
                                     ele, ex);
        }
    }
    // 解析后进行监听器激活处理
    Resource[] actResArray = actualResources.toArray(new Resource[0]);
    getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

在解析 标签时, Spring进行解析的步骤大致如下:

  • 获取 resource 属性所表示的路径;
  • 解析路径中的系统属性,格式如 “${user.dir}”
  • 判定 resource 属性是绝对路径还是相对路径;
  • 如果是绝对路径则递归调用 bean 的解析过程,进行另一次的解析;
  • 如果是相对路径则计算出绝对路径并进行解析;
  • 通知监听器,解析完成。

关于递归调用 bean 的解析过程,其实是再次调用 loadBeanDefinitions(relativeResource) 方法,该方法在上一篇文章中已经阐述,这里就不多说了。

⑤. 嵌入式beans标签的解析

嵌入式的 标签一般很少使用,这也是解析默认标签的四个中最后一个。使用这个标签的方法如下:

<?xml version='1.0' encoding='UTF-8' ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myTestBean" class="guo.ping.ioc.bean.MyTestBean">
        <property name="testStr" value="haha" />
    </bean>

    <beans>
        <!--嵌入式beans-->
    </beans>
</beans>

Spring对于嵌入式 标签的解析利用了 doRegisterBeanDefinitions(ele) 方法,同样该方法还是很眼熟,没错,其实就是上一篇文章的最后部分,依据配置文件解析注册 beanDefinition 环节。40行代码也就是本篇文章以及下一篇文章一直在分析的方法。

/**
 * 根据<beans>...</beans>注册所有的bean,核心之处
 * Register each bean definition within the given root {@code <beans/>} element.
 */
@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.

    // 专门用于处理解析
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    if (this.delegate.isDefaultNamespace(root)) {
        // 处理profile属性,profile属性是配置部署环境的(生产、开发),方便切换环境
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            // profile可以同时指定多个,需要进行拆分,并解析每个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.
            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;
            }
        }
    }

    // 解析前处理,留给子类实现
    preProcessXml(root);
    // 解析.... 你懂的,继续前进:)
    parseBeanDefinitions(root, this.delegate);
    // 解析后处理,留给子类实现
    postProcessXml(root);

    this.delegate = parent;
}