constructor-arg子元素的解析
對構造函數的解析是非常常用的,同時也是非常復雜的,也相信大家對構造函數的配置配置都不陌生,舉一個簡單的例子來開始對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.xsdhttp://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子元素的解析