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