1. 程式人生 > >constructor-arg子元素的解析

constructor-arg子元素的解析

ida 通過 就是 ear 允許 text style 根據 values

對構造函數的解析是非常常用的,同時也是非常復雜的,也相信大家對構造函數的配置配置都不陌生,舉一個簡單的例子來開始對constructor-arg的解析。

1. User.java

public class User implements Serializable{
       /**
        * 註釋內容
        */
       private static final long serialVersionUID = -6199935307945147019L;
       private String name;
       private int age;
       
public User(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; } }

2. xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="
http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="user" class="com.example.demo.entity.User"> <constructor-arg index="0"> <value>zzzzzz</value> </constructor-arg> <constructor-arg index="1"> <value>14</value> </constructor-arg> </bean> </beans>

上面的配置是spring構造函數的基礎配置。我們來看看具體的xml解析過程。

對於constructor-arg子元素的解析,Spring是通過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);
           if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
               parseConstructorArgElement((Element) node, bd);
           }
        }
    }

這個結構似乎可以想象的到,遍歷所有子元素,也就是提取所有constructor-arg然後進行解析,但是具體的解析卻被放置在了另一個函數parseConstructorArgElement中,具體代碼如下:

 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);
        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對應的屬性元素
                       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));
                       // 不允許重復指定相同參數
                       if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
                           error("Ambiguous constructor-arg entries for index " + index, ele);
                       }
                       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 {
           // 沒有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)。

如果配置中指定了index屬性,那麽操作步驟如下。

(1) 解析constructor-arg的子元素。

(2) 使用ConstructorArgumentValues.ValueHolder類型來封裝解析出來的元素。

(3) 將type、name和index屬性一並封裝在ConstructorArgumentValues.ValueHolder類型中並添加至當前BeanDefinition的constructorArgumentValues的IndexedArgumentValues屬性中。

如果沒有指定index屬性,那麽操作步驟如下:

(1) 解析constructor-arg的子元素。

(2) 使用ConstructorArgumentValues.ValueHolder類型來封裝解析出來的元素。

(3) 將type、name和index屬性一並封裝在ConstructorArgumentValues.ValueHolder類型中並添加至當前BeanDefinition的constructorArgumentValues的genericArgumentValues屬性中。

可以看到,對於是否指定index屬性來講,Spring的處理流程是不同的,關鍵在於屬性信息被保存的位置。

了解了整個流程後,我們進一步了解解析構造函數配置中子元素的過程,進入parsePropertyValue:

  public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
        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;
               }
           }
        }
        // 解析constructor-arg上的ref屬性。
        boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
        // 解析constructor-arg上的value屬性
        boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
        if ((hasRefAttribute && hasValueAttribute) ||
               ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
           // 在constructor-arg上不存在:1、同時既有ref屬性又有value屬性;2、存在ref屬性或者value屬性且又有子元素。
           error(elementName +
                   " is only allowed to contain either ‘ref‘ attribute OR ‘value‘ attribute OR sub-element", ele);
        }
 
        if (hasRefAttribute) {
           // ref屬性的處理,使用RuntimeBeanReference封裝對應的ref名稱。
           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;
        }
        else if (hasValueAttribute) {
           // value屬性的處理,使用TypedStringValue封裝。
           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也沒有子元素,Spring就會報錯.
           error(elementName + " must specify a ref or value", ele);
           return null;
        }
    }

從代碼上來看,對構造函數中屬性元素的解析,經理了以下幾個過程。

(1) 略過description或者meta。

(2) 提取constructor-arg上的ref和value屬性,以便於根據規則驗證正確性,其規則為在constructor-arg上不存在以下情況。

同時既有ref屬性又有value屬性。

存在ref屬性或者value屬性且又有子元素

(3) Ref屬性的處理。使用RuntimeBeanReference封裝對應的ref名稱。如:

<constructor-arg ref=“a” >

(4) value屬性的處理。使用TypedStringValue封裝,例如:

<constructor-arg value=“a” >

(5) 子元素的處理。例如:

<constructor-arg>

<map>

<entry key=”key” value=”value” />

</map>

</ constructor-arg>

parsePropertySubElement中實現了對各種子元素的分類處理。

  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);
        }
        else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
           BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
           if (nestedBd != null) {
               nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
           }
           return nestedBd;
        }
        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)) {
               // 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)) {
           // 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;
        }
    }

可以看到,在上面的函數中實現了所有可支持的子類的分類處理。

constructor-arg子元素的解析