zoukankan      html  css  js  c++  java
  • 3.6 spring-construction-arg 子元素的使用与解析

      对于构造函数子元素是非常常用的. 相信大家也一定不陌生,

    举个小例子:

      

     1 public class Animal {
     2 
     3     public String type;
     4 
     5     public int age;
     6 
     7     /**
     8      * @param type
     9      * @param age
    10      */
    11     public Animal(String type, int age) {
    12         super();
    13         this.type = type;
    14         this.age = age;
    15     }
    16 
    17     /*
    18      * (non-Javadoc)
    19      * 
    20      * @see java.lang.Object#toString()
    21      */
    22     @Override
    23     public String toString() {
    24         return "Animal [type=" + type + ", age=" + age + "]";
    25     }
    26 }

    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"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
        <bean id="animal" class="test.constructor.Animal">
            <constructor-arg index="0" value="cat" type="String"></constructor-arg>
            <constructor-arg value="100" ></constructor-arg>
        </bean>
    </beans> 

    测试类如下:

     1 public class Main {
     2 
     3     public static String XML_PATH = "test\constructor\applicationContxt.xml";
     4 
     5     public static void main(String[] args) {
     6         try {
     7             Resource resource = new ClassPathResource(XML_PATH);
     8             XmlBeanFactory beanFactory = new XmlBeanFactory(resource);
     9             Animal bean = (Animal) beanFactory.getBean("animal");
    10             System.out.println(bean);
    11         }
    12         catch (Exception e) {
    13             e.printStackTrace();
    14         }
    15     }
    16 }

      对于construction-arg 子元素的解析,Spring 是通过 BeanDefinitionParserDelegate. parseConstructorArgElements(Element beanEle, BeanDefinition bd); 方法来实现的,

    具体代码如下,

     1 /**
     2      * Parse constructor-arg sub-elements of the given bean element.
     3      */
     4     public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
     5         NodeList nl = beanEle.getChildNodes();
     6         for (int i = 0; i < nl.getLength(); i++) {
     7             Node node = nl.item(i);
     8             if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
     9                 // parseConstructorArgElement
    10                 parseConstructorArgElement((Element) node, bd);
    11             }
    12         }
    13     }

    追踪下去如下:

     1 /**
     2      * Parse a constructor-arg element.
     3      */
     4     public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
     5         // 提取 Index
     6         String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
     7         // 提取 type
     8         String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
     9         // 提取 name
    10         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    11         if (StringUtils.hasLength(indexAttr)) {
    12             try {
    13                 int index = Integer.parseInt(indexAttr);
    14                 if (index < 0) {
    15                     error("'index' cannot be lower than 0", ele);
    16                 }
    17                 else {
    18                     try {
    19                         this.parseState.push(new ConstructorArgumentEntry(index));
    20                         // 对ele元素的解析
    21                         Object value = parsePropertyValue(ele, bd, null);
    22                         ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(
    23                                 value);
    24                         if (StringUtils.hasLength(typeAttr)) {
    25                             valueHolder.setType(typeAttr);
    26                         }
    27                         if (StringUtils.hasLength(nameAttr)) {
    28                             valueHolder.setName(nameAttr);
    29                         }
    30                         valueHolder.setSource(extractSource(ele));
    31                         // 不允许重复指定相同的参数
    32                         if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(
    33                                 index)) {
    34                             error("Ambiguous constructor-arg entries for index " + index,
    35                                     ele);
    36                         }
    37                         else {
    38                             // 对于有Index 的参数放这里
    39                             bd.getConstructorArgumentValues().addIndexedArgumentValue(
    40                                     index, valueHolder);
    41                         }
    42                     }
    43                     finally {
    44                         this.parseState.pop();
    45                     }
    46                 }
    47             }
    48             catch (NumberFormatException ex) {
    49                 error("Attribute 'index' of tag 'constructor-arg' must be an integer",
    50                         ele);
    51             }
    52         }
    53         else {
    54             // 如果没有index 属性则忽略去属性,自动寻找
    55             try {
    56                 this.parseState.push(new ConstructorArgumentEntry());
    57                 Object value = parsePropertyValue(ele, bd, null);
    58                 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(
    59                         value);
    60                 if (StringUtils.hasLength(typeAttr)) {
    61                     valueHolder.setType(typeAttr);
    62                 }
    63                 if (StringUtils.hasLength(nameAttr)) {
    64                     valueHolder.setName(nameAttr);
    65                 }
    66                 valueHolder.setSource(extractSource(ele));
    67                 // 对于没Index参数的放这里
    68                 bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
    69             }
    70             finally {
    71                 this.parseState.pop();
    72             }
    73         }
    74     }

    上面一段代码虽然长,但涉及的逻辑其实并不复杂.

    1.首先是提取 constructor-arg 上的必要属性,(index,name,type);

    如果是指定了index属性的 

      1,解析constructor-arg 的子元素

      2,使用 ConstructorArgumentValues.ValueHolder来封装解析出来的数据,

      3,将 index,type,name 一起封装在当前beandefinition 的 IndexedArgumentValues 属性当中

    如果没有指定index属性的

      1,解析constructor-arg 的子元素

      2,使用 ConstructorArgumentValues.ValueHolder来封装解析出来的数据,

      3,将 index,type,name 一起封装在当前beandefinition 的 GenericArgumentValues 属性当中

    这里可以看对于是否指定index 参数的,Spring的处理流程是不同的,关键在于保存的位置不同

    那么了解这个过程之后,我们尝试进一步了解解析构造函数配置中解析子元素的过程,进入 parsePropertyValue

    代码如下:

     1 /**
     2      * Get the value of a property element. May be a list etc. Also used for constructor
     3      * arguments, "propertyName" being null in this case.
     4      */
     5     /**
     6      * @param ele
     7      * @param bd
     8      * @param propertyName
     9      * @return
    10      */
    11     public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
    12         String elementName = (propertyName != null) ? "<property> element for property '"
    13                 + propertyName + "'" : "<constructor-arg> element";
    14 
    15         // Should only have one child element: ref, value, list, etc.
    16         // 应该只有一个子元素:REF,值,列表等。
    17         NodeList nl = ele.getChildNodes();
    18         Element subElement = null;
    19         for (int i = 0; i < nl.getLength(); i++) {
    20             Node node = nl.item(i);
    21             // 对应的description 或者meta不处理
    22             if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)
    23                     && !nodeNameEquals(node, META_ELEMENT)) {
    24                 // Child element is what we're looking for.
    25                 if (subElement != null) {
    26                     error(elementName + " must not contain more than one sub-element",
    27                             ele);
    28                 }
    29                 else {
    30                     subElement = (Element) node;
    31                 }
    32             }
    33         }
    34         // 解析 ref
    35         boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
    36         // 解析 value
    37         boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
    38         if ((hasRefAttribute && hasValueAttribute)
    39                 || ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
    40             /*
    41              * 1.不能同时有ref 又有 value 2.不能存在ref 或者 value 又有子元素
    42              */
    43             error(elementName
    44                     + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element",
    45                     ele);
    46         }
    47 
    48         if (hasRefAttribute) {
    49             String refName = ele.getAttribute(REF_ATTRIBUTE);
    50             if (!StringUtils.hasText(refName)) {
    51                 error(elementName + " contains empty 'ref' attribute", ele);
    52             }
    53             // ref 属性的处理 , 使用RuntimeBeanReference封装对应的ref名称
    54             RuntimeBeanReference ref = new RuntimeBeanReference(refName);
    55             ref.setSource(extractSource(ele));
    56             return ref;
    57         }
    58         else if (hasValueAttribute) {
    59             // Value 属性的处理 , 使用TypedStringValue封装对应的
    60             TypedStringValue valueHolder = new TypedStringValue(
    61                     ele.getAttribute(VALUE_ATTRIBUTE));
    62             valueHolder.setSource(extractSource(ele));
    63             return valueHolder;
    64         }
    65         else if (subElement != null) {
    66             // 解析子元素
    67             return parsePropertySubElement(subElement, bd);
    68         }
    69         else {
    70             // Neither child element nor "ref" or "value" attribute found.
    71             // 对于没有ref 也没有子元素的,Spring只好丢出异常
    72             error(elementName + " must specify a ref or value", ele);
    73             return null;
    74         }
    75     }

    从上面的代码上看,对构造函数中属性的解析,经历了以下几个过程:

      1. 略过description 和 meta

      2. 提取 ref ,value 属性,并验证其合法性,

      3. ref 属性的处理,使用 RuntimeBeanReference 封装

      4. Value 属性的处理 , 使用TypedStringValue封装

      5. 子属性的处理 如 

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

      而对于子元素,则交给 parsePropertySubElement 方法来实现对各种子元素进行分类处理

    代码如下:

    1 public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
    2         return parsePropertySubElement(ele, bd, null);
    3     }

    继续如下:

     1 /**
     2      * Parse a value, ref or collection sub-element of a property or constructor-arg
     3      * element.
     4      * 
     5      * @param ele subelement of property element; we don't know which yet
     6      * @param defaultValueType the default type (class name) for any {@code &lt;value&gt;}
     7      *        tag that might be created
     8      */
     9     public Object parsePropertySubElement(Element ele, BeanDefinition bd,
    10             String defaultValueType) {
    11         if (!isDefaultNamespace(ele)) {
    12             return parseNestedCustomElement(ele, bd);
    13         }
    14         else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
    15             BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
    16             if (nestedBd != null) {
    17                 nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
    18             }
    19             return nestedBd;
    20         }
    21         else if (nodeNameEquals(ele, REF_ELEMENT)) {
    22             // A generic reference to any name of any bean.
    23             String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
    24             boolean toParent = false;
    25             if (!StringUtils.hasLength(refName)) {
    26                 // A reference to the id of another bean in the same XML file.
    27                 // 解析本地资源
    28                 refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
    29                 if (!StringUtils.hasLength(refName)) {
    30                     // A reference to the id of another bean in a parent context.
    31                     // 解析父类资源
    32                     refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
    33                     toParent = true;
    34                     if (!StringUtils.hasLength(refName)) {
    35                         error("'bean', 'local' or 'parent' is required for <ref> element",
    36                                 ele);
    37                         return null;
    38                     }
    39                 }
    40             }
    41             if (!StringUtils.hasText(refName)) {
    42                 error("<ref> element contains empty target attribute", ele);
    43                 return null;
    44             }
    45             RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
    46             ref.setSource(extractSource(ele));
    47             return ref;
    48         }
    49         // 对idref 的处理
    50         else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
    51             return parseIdRefElement(ele);
    52         }
    53         // 对value 的处理
    54         else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
    55             return parseValueElement(ele, defaultValueType);
    56         }
    57         // 对 null 的处理
    58         else if (nodeNameEquals(ele, NULL_ELEMENT)) {
    59             // It's a distinguished null value. Let's wrap it in a TypedStringValue
    60             // object in order to preserve the source location.
    61             TypedStringValue nullHolder = new TypedStringValue(null);
    62             nullHolder.setSource(extractSource(ele));
    63             return nullHolder;
    64         }
    65         // 对array 的处理
    66         else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
    67             return parseArrayElement(ele, bd);
    68         }
    69         // 对list 的处理
    70         else if (nodeNameEquals(ele, LIST_ELEMENT)) {
    71             return parseListElement(ele, bd);
    72         }
    73         // 对set 的处理
    74         else if (nodeNameEquals(ele, SET_ELEMENT)) {
    75             return parseSetElement(ele, bd);
    76         }
    77         // 对map 的处理
    78         else if (nodeNameEquals(ele, MAP_ELEMENT)) {
    79             return parseMapElement(ele, bd);
    80         }
    81         // 对props 的处理
    82         else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
    83             return parsePropsElement(ele);
    84         }
    85         else {
    86             error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
    87             return null;
    88         }
    89     }

    可以看到,上面方法中实现了所有可支持的子类的分类处理,到这里,我们已经大致清楚构造函数的处理流程了,至于在深入的解析,这里不做解析.

  • 相关阅读:
    18.10.29 考试总结
    【BZOJ】2730: [HNOI2012]矿场搭建
    BZOJ [ZJOI2007]仓库建设
    18.10.24 考试总结
    ZOJ 3740 Water Level
    洛谷 P2474 [SCOI2008]天平
    洛谷 P4180 【模板】严格次小生成树[BJWC2010]
    CF961E Tufurama
    18.10.22 考试总结
    18.10.19 考试总结
  • 原文地址:https://www.cnblogs.com/mjorcen/p/3647375.html
Copyright © 2011-2022 走看看