对于构造函数子元素是非常常用的. 相信大家也一定不陌生,
举个小例子:
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 <value>} 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 }
可以看到,上面方法中实现了所有可支持的子类的分类处理,到这里,我们已经大致清楚构造函数的处理流程了,至于在深入的解析,这里不做解析.