首页 > 技术文章 > Spring IoC 默认标签解析

leisurexi 2020-04-21 23:45 原文

前言

本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。

本篇文章主要介绍 Spring IoC 容器怎么解析默认标签的。

正文

所谓的默认标签就是 importaliasbeanbeans 这四个标签。

DefaultBeanDefinitionDocumentReader#parseDefaultElement

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)) {
        doRegisterBeanDefinitions(ele);
    }
}

上面 parseDefaultElement 方法中对 bean 标签的处理方法 processBeanDefinition 最为重要,下面来着重分析一下。

DefaultBeanDefinitionDocumentReader#processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 将ele解析成BeanDefinitionHolder,见下文详解
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 若存在默认标签下的子节点下不再有自定义属性,需要再次对自定义标签再进行解析(基本不用,不做深入分析)
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 注册最终的BeanDefinition,见下文详解
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" +
                                     bdHolder.getBeanName() + "'", ele, ex);
        }
        // 发出响应事件,通知相关监听器,这个bean已经注册完
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

BeanDefinitionParseDelegate#parseBeanDefinitionElement

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    // 解析元素,封装成BeanDefinitionHolder
    return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // 获取id属性
    String id = ele.getAttribute(ID_ATTRIBUTE);
    // 获取name属性
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    List<String> aliases = new ArrayList<>();
    // 将name属性所有的名称按照逗号或者分号(,;),分割成数组放入别名集合aliases
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }
    // beanName默认使用id
    String beanName = id;
    // 没有指定id属性 && 指定了name属性
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
        // 如果没有指定id,beanName等于第一个别名,剩下的依然作为别名使用
        beanName = aliases.remove(0);
    }

    if (containingBean == null) {
        // 验证beanName和aliases是否在同一个<beans>下已经存在
        checkNameUniqueness(beanName, aliases, ele);
    }
    // 将元素解析成GenericBeanDefinition
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    if (beanDefinition != null) {
        // 如果不存在beanName会根据Spring的命名规则生成一个
        if (!StringUtils.hasText(beanName)) {
            try {
                if (containingBean != null) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                }
                else {
                    beanName = this.readerContext.generateBeanName(beanDefinition);
                    String beanClassName = beanDefinition.getBeanClassName();
                    if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                        aliases.add(beanClassName);
                    }
                }
            }
            catch (Exception ex) {
                error(ex.getMessage(), ele);
                return null;
            }
        }
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        // 用beanDefinition和beanName以及aliasesArray构建BeanDefinitionHolder
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    return null;
}

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {

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

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

    try {
        // 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);
        // 解析默认bean的各种属性,见下文详解
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        // 提取description
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
        // 解析元数据,见下文详解
        parseMetaElements(ele, bd);
        // 解析lookup-method属性,很少使用,不具体介绍
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // 解析replaced-method属性,很少使用,不具体介绍
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        // 解析constructor-arg属性,见下文详解
        parseConstructorArgElements(ele, bd);
        // 解析property属性,见下文详解
        parsePropertyElements(ele, bd);
        // 解析qualifier属性,见下文详解
        parseQualifierElements(ele, bd);
       
        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));
        return bd;
    }
    // 省略异常处理...
    finally {
        this.parseState.pop();
    }
    return null;
}

上面方法中的 BeanDefinitionReaderUtils.generateBeanName() 方法会为 bean 生成一个默认的名称,主要规则如下:

  1. 获取 bean 的全类名,例如 com.leisurexi.ioc.domain.User;如果 beanparent 不为空那么 bean 的名称为 parentName 加上 $child,例如 userparent 是名称为 parentUserbean,那么当 user 未指定 beanName 时会其生成一个 parentUser$child 的名称;如果 beanparent 为空但 factory-bean 属性不为空,那就用该名称加上 $created 为其生成名称。
  2. 未指定 parentfactory-bean 属性,那么如果是内嵌 bean 则用全类名加上 # 和转换为十六进制字符串的 hashcode 拼成的字符串当做名称;不是内嵌 bean 就用全类名加上 # 和数字当做名称,如第一个 User 类型的自动生成的名称为 com.leisurexi.ioc.domain.User#0,第二个就是 com.leisurexi.ioc.domain.User#1

我们这边可以简单看一下 BeanDefinitionHolder 的属性,如下:

public class BeanDefinitionHolder implements BeanMetadataElement {
	
    // bean 的定义元信息
    private final BeanDefinition beanDefinition;
    // bean 的名称
    private final String beanName;
    // bean 的别名数组
    @Nullable
    private final String[] aliases;

    // 省略其它代码...
}

BeanDefinitionParserDelegate#parseBeanDefinitionAttributes

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
    // 解析singleton属性
    if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
        // singleton属性已经不支持了,使用了会直接抛出异常,请使用scope属性替代
        error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
    }
    // 解析scope属性
    else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
        bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
    }
    // 如果是内嵌bean,则使用上级bean的scope值
    else if (containingBean != null) {
        bd.setScope(containingBean.getScope());
    }
    // 解析abstract属性
    if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
        bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
    }
    // 解析lazy属性
    String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
    // 若没有设置或者设置成其他字符都会被设置为默认值false
    if (isDefaultValue(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属性,该属性为false代表该bean不会被选为依赖注入的对象,默认为true
    String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
    if (isDefaultValue(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);
    }
    // 如果bean没有指定init-method属性,但beans标签指定了default-init-method属性,则会使用该属性
    else if (this.defaults.getInitMethod() != null) {
        bd.setInitMethodName(this.defaults.getInitMethod());
        bd.setEnforceInitMethod(false);
    }
    // 解析destroy-method属性
    if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
        String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
        bd.setDestroyMethodName(destroyMethodName);
    }
    // 如果bean没有指定destroy-method属性,但beans标签指定了default-destroy-method属性,则会使用该属性
    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;
}

上面方法完成了对所有 bean 标签属性的解析。值得注意的地方是如果同时指定了 bean 标签的 init-methodbeans 标签的 default-init-method 属性,那么优先使用前者,destory-mehtod 标签也是一样。

大家可以去看一下 AbstractBeanDefinition 中定义的属性就一目了然了,这里限于篇幅原因就不展示了。

BeanDefinitionParserDelegate#parseConstructorArgElements

public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
    // 获取所有子节点
    NodeList nl = beanEle.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        // 提取 constructor-arg
        if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
            // 解析 constructor-arg
            parseConstructorArgElement((Element) node, bd);
        }
    }
}
// <constructor-arg index="0" type="" value=""/>
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
    // 提取 index 属性
    String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
    // 提取 type 属性
    String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
    // 提取 name 属性
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    // index 不为空
    if (StringUtils.hasLength(indexAttr)) {
        try {
            // 转换为 int 类型
            int index = Integer.parseInt(indexAttr);
            if (index < 0) {
                error("'index' cannot be lower than 0", ele);
            }
            else {
                try {
                    this.parseState.push(new ConstructorArgumentEntry(index));
                    // 解析属性值,见下文详解
                    Object value = parsePropertyValue(ele, bd, null);
                    // 使用 ConstructorArgumentValues.ValueHolder 类型来封装解析出来的元素
                    ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                    // 如果 type 不为空,设置 ValueHolder 的 type
                    if (StringUtils.hasLength(typeAttr)) {
                        valueHolder.setType(typeAttr);
                    }
                    // 如果 name 不为空,设置 ValueHolder 的 name
                    if (StringUtils.hasLength(nameAttr)) {
                        valueHolder.setName(nameAttr);
                    }
                    valueHolder.setSource(extractSource(ele));
                    // 判断 index 是否重复,重复则抛出异常
                    if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {							
                        error("Ambiguous constructor-arg entries for index " + index, ele);
                    }
                    // 将 index 和 valueHolder 以 key-value的形式 添加到 BeanDefinition 的 ConstructorArgumentValues 当中
                    else {
                        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 {
        try {
            // 这里就是 constructor-arg 标签中没有指定 index 属性
            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));
            // 将 valueHolder 添加 BeanDefinition 的 GenericArgumentValue 中
            // 这里和上面的 IndexedArgumentValue 类似,只不过上面是 Map,这里是 List
            bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
        }
        finally {
            this.parseState.pop();
        }
    }
}

上面代码首先提取 constructor-arg 标签中必要的属性 (indextypename)。

  • 如果指定了 index 属性:
    1. 解析 constructor-arg 的子元素。
    2. 使用 ConstructorArgumentsValues.ValueHolder 类型来封装解析出来的元素。
    3. typenameindex 属性一并封装在 ConstructorArgumentsValues.ValueHolder 类型中,并添加到当前 BeanDefinitionConstructorArgumentValues 中的 LinkedHashMap 类型的属性indexedArgumentValues 中。
  • 如果没有指定 index 属性:
    1. 解析 constructor-arg 的子元素。
    2. 使用 ConstructorArgumentsValues.ValueHolder 类型来封装解析出来的元素。
    3. typenameindex 属性一并封装在 ConstructorArgumentsValues.ValueHolder 类型中,并添加到当前 BeanDefinitionConstructorArgumentValues 中的 ArrayList 类型的属性genericArgumentValues 中。

BeanDefinitionParserDelegate#parsePropertyValue

public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
    String elementName = (propertyName != null ?
                          "<property> element for property '" + propertyName + "'" :
                          "<constructor-arg> element");
    // 获取所有子节点,例如list、map等
    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 属性
    boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
    // 提取 value 属性
    boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
    // 如果同时有 ref 和 value 属性 || 有 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);
    }
    // 只有 ref 属性,使用 RuntimeBeanReference 封装对应的 ref 名称 (该 ref 值指向另一个 bean 的 beanName)
    // RuntimeBeanReference 起到占位符的作用, ref 指向的 beanName 将在运行时被解析成真正的 bean 实例引用
    if (hasRefAttribute) {
        String refName = ele.getAttribute(REF_ATTRIBUTE);
        if (!StringUtils.hasText(refName)) {
            error(elementName + " contains empty 'ref' attribute", ele);
        }
        RuntimeBeanReference ref = new RuntimeBeanReference(refName);
        ref.setSource(extractSource(ele));
        return ref;
    }
    // 只有 value 属性,使用 TypedStringValue 封装
    else if (hasValueAttribute) {
        TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
        valueHolder.setSource(extractSource(ele));
        return valueHolder;
    }
    else if (subElement != null) {
        // 解析子元素
        return parsePropertySubElement(subElement, bd);
    }
    else {
        // 没有子元素,也没有 ref 和 value,直接抛出异常
        error(elementName + " must specify a ref or value", ele);
        return null;
    }
}

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
    return parsePropertySubElement(ele, bd, null);
}

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)) {
        String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
        boolean toParent = false;
        if (!StringUtils.hasLength(refName)) {
            // A reference to the id of another bean in a parent context.
            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)) {
        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;
    }
}

从上面的代码来看,对构造函数中属性元素的解析,步骤如下:

  1. 略过 description 或者 meta
  2. 提取 constructor-arg 上的 refvalue 属性,以便于根据规则验证正确性。其规则为在 constructor-arg 上不存在以下情况:
    • 同时存在 refvalue 属性。
    • 存在 ref 或者 value 属性,并且又有子元素。

BeanDefinitionParserDelegate#parsePropertyElements

public void parsePropertyElements(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, PROPERTY_ELEMENT)) {
            // 解析 property 节点
            parsePropertyElement((Element) node, bd);
        }
    }
}

// 这里是解析 property 标签,<property name="..." value="..."/>
public void parsePropertyElement(Element ele, BeanDefinition bd) {
    // 获取 name 属性
    String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
    // name 为空,抛出异常
    if (!StringUtils.hasLength(propertyName)) {
        error("Tag 'property' must have a 'name' attribute", ele);
        return;
    }
    this.parseState.push(new PropertyEntry(propertyName));
    try {
        // 出现两个 name 相同的抛出异常
        if (bd.getPropertyValues().contains(propertyName)) {
            error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
            return;
        }
        // 解析属性值,跟构造器解析一样
        Object val = parsePropertyValue(ele, bd, propertyName);
        // 用 name 和 val 封装成 PropertyValue
        PropertyValue pv = new PropertyValue(propertyName, val);
        // 解析元数据,跟 beans 标签内的 meta 一样
        parseMetaElements(ele, pv);
        pv.setSource(extractSource(ele));
        // 添加到 BeanDefinition 的 PropertyValues 属性中
        bd.getPropertyValues().addPropertyValue(pv);
    }
    finally {
        this.parseState.pop();
    }
}

上面方法主要是遍历 property 节点,然后解析属性值封装成 PropertyValue 添加到 BeanDefinitionPropertyValues 中。

注意:property 标明的属性必需有 set 方法否则在赋值阶段会抛出异常。

BeanDefinitionParserDelegate#parseQualifierElements

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)) {
            // 解析 qualifier 节点
            parseQualifierElement((Element) node, bd);
        }
    }
}

public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
    // 提取 type
    String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
    // type 为空抛出异常
    if (!StringUtils.hasLength(typeName)) {
        error("Tag 'qualifier' must have a 'type' attribute", ele);
        return;
    }
    this.parseState.push(new QualifierEntry(typeName));
    try {
        AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
        qualifier.setSource(extractSource(ele));
        // 提取 value
        String value = ele.getAttribute(VALUE_ATTRIBUTE);
        // value 不为空,设置到 AutowireCandidateQualifier 的 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);
            // 如果是有 attribute 节点,进行解析,提取值放入到 AutowireCandidateQualifier 的MetadataAttribute 中
            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;
                }
            }
        }
        // 设置 BeanDefinition 的 qualifier
        bd.addQualifier(qualifier);
    }
    finally {
        this.parseState.pop();
    }
}

对于 qualifier 元素的获取,我们大多数接触的更多是注解的形式,在使用 Spring 框架中进行自动注入时,Spring 容器中匹配的候选 Bean 必需有且只有一个。如果存在多个类型相同的 Bean,且按照类型注入时,Spring 允许通过 qualifier 指定注入 Bean 的名称,这样歧义就消除了。

BeanDefinitionReaderUtils#registerBeanDefinition

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {

    // Register bean definition under primary name.
    // 获取 beanName
    String beanName = definitionHolder.getBeanName();
    // 以 key-value 的形式注册,key 为 beanName,value 为 BeanDefinition。见下文详解
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // Register aliases for bean name, if any.
    // 注册 bean 的别名
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            // 以 key-value 的形式注册 bean 的别名,key 为别名,value 为 beanName。见下文详解
            registry.registerAlias(beanName, alias);
        }
    }
}

// DefaultListableBeanFactory.java
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 {
            // 验证 Bean 的格式是否正确
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                   "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    // 这里判断 BeanDefinition 是否存在
    if (existingDefinition != null) {
        // 这里就是如果 Bean 定义以及存在,判断是否可以覆盖,默认是可以的
        // Spring Boot 2.1开始这里会手动设置 allowBeanDefinitionOverriding 的值为 false
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        // 将 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 缓存中
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
        // bean 是否已经开始创建
        if (hasBeanCreationStarted()) {
            synchronized (this.beanDefinitionMap) {
                // 将 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 缓存中
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 这里将 beanDefinitionNames 写时复制一份,类似于 CopyOnWriteArrayList
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                // 从单例 Bean 注册名称列表中删除当前 beanName
                removeManualSingletonName(beanName);
            }
        }
        // bean 不在创建状态中
        else {
            // Still in startup registration phase
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 因为 ConcurrentHashMap 是无序的,这里将 beanName 放入 ArrayList,记录注册顺序
            this.beanDefinitionNames.add(beanName);
            // 从单例 Bean 注册名称列表中删除当前 beanName
            removeManualSingletonName(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }
    // 如果存在相同的 beanName 的 BeanDefinition,或者 beanName 已经存在单例对象,则将该 beanName 对应的缓存信息、单例对象清除,因为这些对象都是由老的 BeanDefinition 创建的,需要被覆盖掉。再用新的 BeanDefinition 来创建这些缓存和单例对象
    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

// SimpleAliasRegistry.java
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 相同,从缓存中移除
        if (alias.equals(name)) {
            this.aliasMap.remove(alias);
        }
        else {
            String registeredName = this.aliasMap.get(alias);
            // 如果别名以及注册过,直接返回
            if (registeredName != null) {
                if (registeredName.equals(name)) {
                    // An existing alias - no need to re-register
                    return;
                }
                // 如果不允许覆盖,抛出异常
                if (!allowAliasOverriding()) {
                    throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
                }
            }
            // 检查 name 和 alias 是否存在循环引用。例如 A 的别名为 B,B的别名为A
            checkForAliasCircle(name, alias);
            // 将 alias 和 name 以 key-value 对放入到 aliasMap 中,进行缓存
            this.aliasMap.put(alias, name);
            }
        }
    }
}

上面代码有两个变量比较重要 beanDefinitionMapbeanDefinitionNames,下面代码是这两个属性在 DefaultListableBeanFactory 中的定义:

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
    // 缓存 BeanDefinition 的 Map,key 为 beanName,value 为 BeanDefinition
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    // 保存 BeanDefinition 的注册顺序,保存的是 beanName
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
    
}

上面方法的主要流程如下:

  1. 如果 BeanDefinitionAbstractBeanDefinition 类型,验证 Bean 的格式是否正确。

    这次效验主要是对于 AbstractBeanDefinition 属性中的 methodOverrides 的校验,校验 methodOverrides 是否与 工厂方法 并存或者 methodOverrides 中的方法根本不存在。

  2. 判断该 beanNameBeanDefinition 是否已经注册过;如果存在判断是否允许覆盖,允许的话直接替换,不允许直接抛出异常。

    默认的情况下是允许的,但是在 Spring Boot 2.1 开始这里会手动的设置为不允许。

  3. beanName 对应的 BeanDefinition 以前没有注册过,判断 bean 是否已经开始创建;如果在创建中对 beanDefinitionMap 进行加锁 (这里虽然 beanDefinitionMap 是线程安全的 ConcurrentHashMap ,单个操作是线程安全的但多个操作不是,所以这里手动加锁),然后将 beanNameBeanDefinitionkey-value 形式放入 beanDefinitionMap 缓存中,然后写时复制一份 beanDefinitionNames ,将 beaName 缓存进去,记录 bean 的注册顺序;如果不在创建中直接将 BeanDefinitionbeanName 分别放入 beanDefinitionMapbeanDefinitionNames 中。

  4. 最后判断如果 BeanDefinition 已经注册过,或者 beanName 已经存在单例对象,则将该 beanName 对应的缓存信息、单例对象清除,因为这些对象都是由老的 BeanDefinition 创建的,需要被覆盖掉。再用新的 BeanDefinition 来创建这些缓存和单例对象。

总结

本文主要介绍了 Spring 对 XML 文件中 <bean> 标签的解析,我们可以重新梳理一下思路:

  1. 解析 <bean> 标签,构建成 AbstractBeanDefinition (GenericBeanDefinition) 对象来存放所有解析出来的属性。

  2. AbstractBeanDefinitionbeanNamealiasesArray 构建成 BeanDefinitionHolder 对象。

  3. 最后通过 BeanDefinitionHolderbeanNameBeanDefinition 注册到 DefaultListableBeanFactory 中,也就是保存起来。

    上文提到的两个比较重要的属性 beanDefinitionNamesbeanDefinitionMap ,在后面都会多次用到,可以重点关注一下。

最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址:https://github.com/leisurexi/tiny-spring

参考

推荐阅读