Spring IoC 默認標籤解析

前言

本系列所有基於 Spring 5.2.2.BUILD-SNAPSHOT 版本。由於 Spring 整個體系太過於龐大,因此只會進行關鍵部分的源碼解析。java

本篇文章主要介紹 Spring IoC 容器怎麼解析默認標籤的。node

正文

所謂的默認標籤就是 importaliasbeanbeans 這四個標籤。git

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 最爲重要,下面來着重分析一下。github

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 生成一個默認的名稱,主要規則以下:spring

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

從上面的代碼來看,對構造函數中屬性元素的解析,步驟以下:dom

  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

參考

相關文章
相關標籤/搜索