博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
constructor-arg子元素的解析
阅读量:4955 次
发布时间:2019-06-12

本文共 10843 字,大约阅读时间需要 36 分钟。

  对构造函数的解析是非常常用的,同时也是非常复杂的,也相信大家对构造函数的配置配置都不陌生,举一个简单的例子来开始对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文件

zzzzzz
14

 上面的配置是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) ?                       "
element for property '" + propertyName + "'" : "
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 
element", ele); return null; } } if (!StringUtils.hasText(refName)) { error("
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; } }

 可以看到,在上面的函数中实现了所有可支持的子类的分类处理。

 

 

 

转载于:https://www.cnblogs.com/wcj-java/p/9236030.html

你可能感兴趣的文章
LDAP & implementation
查看>>
iOS - 类扩展与分类的区别
查看>>
AFNetworking 3.0 源码解读(十一)之 UIButton/UIProgressView/UIWebView + AFNetworking
查看>>
启动和停止Java应用程序的Shell脚本
查看>>
CSS选择器之兄弟选择器(~和+)
查看>>
[转]JAVA对象容器
查看>>
使用Spring Boot 和Spring Data JPA访问mysql数据库
查看>>
Syncthing源码解析 - 启动过程
查看>>
python 环境搭建(Mac)
查看>>
隐藏输入法软键盘
查看>>
练习七:列表复制(将一个列表的数据复制到另一个列表中)
查看>>
shell获取本地ip的三种方法
查看>>
百度:人脸登录集成
查看>>
VI使用的小白教程
查看>>
RDD算子
查看>>
从新向你学习javase(第一天)
查看>>
Arduino 9g舵机操作
查看>>
解题报告 逃跑未遂
查看>>
二叉树的遍历算法(js实现)
查看>>
pngcrush caught libpng error: Not a PNG file..
查看>>