spring源码:bean标签的解析之constructor-arg子元素的解析

一、目的

XML中的一个bean标签对应spring中的一个BeanDefinition实例,标签中的各个属性、子元素与BeanDefinition实例中的属性一一对应;解析constructor子元素的目的,就是为了后边spring实例化bean时,可以很容易的提取构造方法需要的信息。本篇就来分析一下spring是如何把bean标签中constructor子元素的信息,解析到BeanDefinition实例中的。

二、映射关系

  1. 先来看下XML中的表示
    <bean id="constructorBean" class="com.kaka.spring.pojo.ConstructorBean">
        <constructor-arg name="a" value="a001"/>
        <constructor-arg name="b" value="a001"/>
    </bean>
  1. spring中对应的载体
    spring会把以上constructor-arg的信息封装至一个ValueHolder实例中,然后保存到AbstractBeanDefinition的constructorArgumentValues属性中。
	@Nullable
	private ConstructorArgumentValues constructorArgumentValues;

该constructorArgumentValues有两种类型:按下标的参数、按参数名的参数

public class ConstructorArgumentValues {

	// 按下标的参数
	private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<>(0);

	// 按参数名的参数
	private final List<ValueHolder> genericArgumentValues = new LinkedList<>();
}

三、相关类、方法

  • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseConstructorArgElement:解析constructor-arg元素的入口
  • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parsePropertyValue:解析constructor-arg中的子元素,校验子元素是否有冲突(如value、ref只能有一个)
  • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parsePropertySubElement(Element, BeanDefinition, String):解析各种子元素(通用方法)

四、源码分析

	// 解析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)) {
				// 解析constructor-arg
				parseConstructorArgElement((Element) node, bd);
			}
		}
	}
	
	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);
						// 创建一个ValueHolder来包装constructor-arg元素信息
						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 {
							// 把valueHolder添加至BeanDefinition的constructorArgumentValues的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 {
			// 没有index属性则忽略去属性,自动寻找
			try {
				this.parseState.push(new ConstructorArgumentEntry());
				Object value = parsePropertyValue(ele, bd, null);
				// 创建一个ValueHolder来包装constructor-arg元素信息
				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));
				// 把valueHolder添加至BeanDefinition的constructorArgumentValues的genericArgumentValues属性中
				bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
			}
			finally {
				this.parseState.pop();
			}
		}
	}

	public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
		String elementName = (propertyName != null) ?
						"<property> element for property '" + propertyName + "'" :
						"<constructor-arg> element";
		// Should only have one child element: ref, value, list, etc.
		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;
				}
			}
		}

		// 是否有ref属性
		boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
		// 是否有value属性
		boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
		// 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);
		}

		// 1.ref属性返回RuntimeBeanReference实例
		if (hasRefAttribute) {
			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;
		}
		// 2.value属性返回TypedStringValue实例
		else if (hasValueAttribute) {
			TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
			valueHolder.setSource(extractSource(ele));
			return valueHolder;
		}
		// 3.解析各种类型的子元素
		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;
		}
	}

	public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
		// 调用重载方法
		return parsePropertySubElement(ele, bd, null);
	}
	
	@Nullable
	public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
		// 解析自定义的子元素
		if (!isDefaultNamespace(ele)) {
			return parseNestedCustomElement(ele, bd);
		}
		// 解析10种默认元素
		// 1.解析bean子元素
		else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
			BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
			if (nestedBd != null) {
				nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
			}
			return nestedBd;
		}
		// 2.解析ref子元素
		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;
		}
		// 3.解析idref子元素
		else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
			return parseIdRefElement(ele);
		}
		// 4.解析value子元素
		else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
			return parseValueElement(ele, defaultValueType);
		}
		// 5.解析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;
		}
		// 6.解析array子元素
		else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
			return parseArrayElement(ele, bd);
		}
		// 7.解析list子元素
		else if (nodeNameEquals(ele, LIST_ELEMENT)) {
			return parseListElement(ele, bd);
		}
		// 8.解析set子元素
		else if (nodeNameEquals(ele, SET_ELEMENT)) {
			return parseSetElement(ele, bd);
		}
		// 9.解析map子元素
		else if (nodeNameEquals(ele, MAP_ELEMENT)) {
			return parseMapElement(ele, bd);
		}
		// 10.解析props子元素
		else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
			return parsePropsElement(ele);
		}
		else {
			error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
			return null;
		}
	}

五、总结

  1. 提取constructor-arg元素中的index、type、name属性。
  2. 解析constructor-arg元素对应的值信息,值信息可以使用value、ref属性或者子元素表示。这三种表示方式,对应以下三种值类型。
    • value属性指定的值—》TypedStringValue
    • ref属性指定的值—》RuntimeBeanReference
    • 子元素对应的值—》Object(根据不同的子元素,返回不同类型的值)
  3. 把解析出的值信息和index、type、name属性值封装到ValueHolder。
  4. 如果指定了index属性,把ValueHolder添加至indexedArgumentValues中;如果未指定index属性,把ValueHolder添加至genericArgumentValues中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值