1. 程式人生 > >【死磕 Spring】—– IOC 之解析 bean 標籤:constructor-arg、property 子元素

【死磕 Spring】—– IOC 之解析 bean 標籤:constructor-arg、property 子元素

上篇部落格(【死磕 Spring】—– IOC 之解析 bean 標籤:meta、lookup-method、replace-method)分析了 meta 、 lookup-method、replace-method 三個子元素,這篇部落格分析 constructor-arg 、property、qualifier 三個子元素。

constructor-arg 子元素

舉個小栗子:

public class StudentService {
    private String name;

    private Integer age;

    private BookService bookService;

    StudentService(String name, Integer age, BookService bookService){
        this.name = name;
        this.age = age;
        this.bookService = bookService;
    }
}

    <bean id="bookService" class="org.springframework.core.service.BookService"/>

    <bean id="studentService" class="org.springframework.core.service.StudentService">
        <constructor-arg index="0" value="chenssy"/>
        <constructor-arg name="age" value="100"/>
        <constructor-arg name="bookService" ref="bookService"/>
    </bean>

StudentService 定義一個建構函式,配置檔案中使用 constructor-arg 元素對其配置,該元素可以實現對 StudentService 自動尋找對應的建構函式,並在初始化的時候將值當做引數進行設定。parseConstructorArgElements() 方法完成 constructor-arg 子元素的解析。

	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、type、name 屬性值 String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 如果有index if (StringUtils.hasLength(indexAttr)) { try { int index = Integer.parseInt(indexAttr); if (index < 0) { error("'index' cannot be lower than 0", ele); } else { try { // 構造一個 ConstructorArgumentEntry 並將其加入到 ParseState 中 this.parseState.push(new ConstructorArgumentEntry(index)); // 解析 ele 對應屬性元素 Object value = parsePropertyValue(ele, bd, null); // 根據解析的屬性元素構造一個 valueHolder 物件 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 { // 加入到 indexedArgumentValues 中國 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 { 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(); } } }

首先獲取 index、type、name 三個屬性值,然後根據是否存在 index 來區分。其實兩者邏輯都差不多,總共分為如下幾個步驟(以有 index 為例):

  1. 構造 ConstructorArgumentEntry 物件並將其加入到 ParseState 佇列中。ConstructorArgumentEntry 表示建構函式的引數。
  2. 呼叫 parsePropertyValue() 解析 constructor-arg 子元素,返回結果值
  3. 根據解析的結果值構造 ConstructorArgumentValues.ValueHolder 例項物件
  4. 將 type、name 封裝到 ConstructorArgumentValues.ValueHolder 中,然後將 ValueHolder 例項物件新增到 indexedArgumentValues 中。

無 index 的處理邏輯差不多,只有幾點不同:構造 ConstructorArgumentEntry 物件時是呼叫無參建構函式;最後是將 ValueHolder 例項新增到 genericArgumentValues 中。

parsePropertyValue() 對子元素進一步解析。

    public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
        String elementName = (propertyName != null) ?
                "<property> element for property '" + propertyName + "'" :
                "<constructor-arg> element";

        NodeList nl = ele.getChildNodes();
        Element subElement = null;
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            // meta 、description 不處理
            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;
                }
            }
        }

        // 解析 ref 元素
        boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
        // 解析 value 元素
        boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);

        // constructor-arg 子元素有兩種情況不存在
        // 1. 即存在 ref 又存在 value
        // 2. 存在 ref 或者 value,但是又有子元素
        if ((hasRefAttribute && hasValueAttribute) ||
                ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
            error(elementName +
                    " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
        }

        if (hasRefAttribute) {
            // 獲取 ref 屬性值
            String refName = ele.getAttribute(REF_ATTRIBUTE);
            if (!StringUtils.hasText(refName)) {
                error(elementName + " contains empty 'ref' attribute", ele);
            }
            // 將 ref 屬性值構造為 RuntimeBeanReference 例項物件
            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 {
            // Neither child element nor "ref" or "value" attribute found.
            error(elementName + " must specify a ref or value", ele);
            return null;
        }
    }
  1. 提取 constructor-arg 子元素的 ref 和 value 的屬性值,對其進行判斷,以下兩種情況是不允許存在的
  • ref 和 value 屬性同時存在
  • 存在 ref 或者 value 且又有子元素
  1. 若存在 ref 屬性,則獲取其值並將其封裝進 RuntimeBeanReference 例項物件中
  2. 若存在 value 屬性,則獲取其值並將其封裝進 TypedStringValue 例項物件中
  3. 如果子元素不為空,則呼叫 parsePropertySubElement() 進行子元素進一步處理

對於 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;
		}
		else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
			return parseIdRefElement(ele);
		}
		else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
			return parseValueElement(ele, defaultValueType);
		}
		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;
		}
		else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
			return parseArrayElement(ele, bd);
		}
		else if (nodeNameEquals(ele, LIST_ELEMENT)) {
			return parseListElement(ele, bd);
		}
		else if (nodeNameEquals(ele, SET_ELEMENT)) {
			return parseSetElement(ele, bd);
		}
		else if (nodeNameEquals(ele, MAP_ELEMENT)) {
			return parseMapElement(ele, bd);
		}
		else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
			return parsePropsElement(ele);
		}
		else {
			error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
			return null;
		}
	}

上面對各個子類進行分類處理,詳細情況如果各位有興趣可以移步原始碼進行深一步的探究。

property 子元素

我們一般使用如下方式來使用 property 子元素。

    <bean id="studentService" class="org.springframework.core.service.StudentService">
        <property name="name" value="chenssy"/>
        <property name="age" value="18"/>
    </bean>

對於 property 子元素的解析,Spring 呼叫 parsePropertyElements()。如下:

	public void parsePropertyElements(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, PROPERTY_ELEMENT)) {
				parsePropertyElement((Element) node, bd);
			}
		}
	}

和 constructor-arg 子元素差不多,同樣是提取所有的 property 的子元素,然後呼叫 parsePropertyElement() 進行分析。

    public void parsePropertyElement(Element ele, BeanDefinition bd) {
        // 獲取 name 屬性
        String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
        if (!StringUtils.hasLength(propertyName)) {
            error("Tag 'property' must have a 'name' attribute", ele);
            return;
        }
        this.parseState.push(new PropertyEntry(propertyName));
        try {
            // 如果存在相同的 name
            if (bd.getPropertyValues().contains(propertyName)) {
                error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
                return;
            }

            // 解析屬性值
            Object val = parsePropertyValue(ele, bd, propertyName);
            // 根據解析的屬性值構造 PropertyValue 例項物件
            PropertyValue pv = new PropertyValue(propertyName, val);
            parseMetaElements(ele, pv);
            pv.setSource(extractSource(ele));
            // 新增到 MutablePropertyValues 中
            bd.getPropertyValues().addPropertyValue(pv);
        }
        finally {
            this.parseState.pop();
        }
    }

與解析 constructor-arg 子元素步驟差不多。呼叫 parsePropertyValue() 解析子元素屬性值,然後根據該值構造 PropertyValue 例項物件並將其新增到 BeanDefinition 中的 MutablePropertyValues 中。