上节说了spring解析replaced-method的过程,虽然在平时的开发中对于它的使用很少,至少我们在这里学到了如何对它的使用,接下来我们来说constructor-arg字标签的使用的case:
import lombok.*;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private String name;
private int age;
接下来我们看相关的配置文件:
<?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.xsd">
<bean id="student" class="com.sgcc.bean.Student">
<constructor-arg index="0" name="name" value="小黑"/>
<constructor-arg index="1" name="age" value="18"/>
</bean>
</beans>
来看测试代码
package com.sgcc.bean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("constructorContext.xml");
Object bean = context.getBean("student");
System.out.println(bean.toString());
}
再来看结果
![](https://img.haomeiwen.com/i3711017/47eedca5839320dd.png)
从上图中我们拿到了我们想要的数据,上述就是constructor-arg的简单用法,接下来我们来看spring对该标签是如何解析的,回归我们的解析默认标签的方法BeanDefinitionParserDelegate#parseConstructorArgElements(),直接看代码:
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);
}
//index> 0时
else {
try {
//保存一个ConstructorArgumentEntry实例对象
this.parseState.push(new ConstructorArgumentEntry(index));
//解析ele对应的属性元素
Object value = parsePropertyValue(ele, bd, null);
//通过解析的属性元素的value构建一个ValueHolder实例
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
//对typeAttr判null处理
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 {
//通过index给指定index的位置上的参数赋值
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
//最后从parseState移除添加的状态
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
//当解析拿到的index为null值时,自动寻找
else {
try {
//1.调用ConstructorArgumentEntry的无参构造器,这里index默认为-1
//2.保存该实例
this.parseState.push(new ConstructorArgumentEntry());
//解析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));
//保存参数
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
//移除该ConstructorArgumentEntry实例
this.parseState.pop();
}
}
}
上述就是解析constructor-arg的过程,这里简单的看一下解析的过程:
- 分别获取index type name属性
public static final String INDEX_ATTRIBUTE = "index";
public static final String TYPE_ATTRIBUTE = "type";
//获取index属性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
//获取type属性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
//获取name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
- 当index不为null时
1.通过获取到的index来构建一个ConstructorArgumentEntry实例
2.通过ParseState#push()保存一个ConstructorArgumentEntry实例对象
this.parseState.push(new ConstructorArgumentEntry(index));
ConstructorArgumentEntry.java
private final int index;
/**
* Creates a new instance of the {@link ConstructorArgumentEntry} class
* representing a constructor argument at the supplied {@code index}.
* @param index the index of the constructor argument
* @throws IllegalArgumentException if the supplied {@code index}
* is less than zero
*/
public ConstructorArgumentEntry(int index) {
Assert.isTrue(index >= 0, "Constructor argument index must be greater than or equal to zero");
this.index = index;
}
ParseState.java
private final LinkedList<Entry> state;
/**
* Add a new {@link Entry} to the {@link LinkedList}.
*/
public void push(Entry entry) {
this.state.push(entry);
- 对ele对应的属性元素
Object value = parsePropertyValue(ele, bd, null);
/**
* Get the value of a property element. May be a list etc.
* Also used for constructor arguments, "propertyName" being null in this case.
*/
@Nullable
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.
//一个属性只能对应一种类型:ref,value list.
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//如果node是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属性或者是有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);
}
//有ref属性
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);
//属性封装source
ref.setSource(extractSource(ele));
return ref;
}
//有value属性
else if (hasValueAttribute) {
//通过获取到的value属性来构建一个TypedStringValue实例对象
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
//source属性的封装
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//有子元素
else if (subElement != null) {
//解析子元素
return parsePropertySubElement(subElement, bd);
}
//既没有ref or value or 子元素
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
以上就是对ele元素的解析的过程 ,在看代码的过程中会发现
- 在解析的过程中,对于获取到的元素如果是description和meta直接不做处理
- 接着是获取ref和value属性
- 分别对各自的属性进行封装处理
- 如果有子元素的情况下,调用parsePropertySubElement对其真正的解析:
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
public static final String BEAN_ELEMENT = "bean";
public static final String REF_ELEMENT = "ref";
public static final String PARENT_REF_ATTRIBUTE = "parent";
public static final String IDREF_ELEMENT = "idref";
public static final String VALUE_ELEMENT = "value";
public static final String NULL_ELEMENT = "null";
public static final String ARRAY_ELEMENT = "array";
public static final String LIST_ELEMENT = "list";
public static final String SET_ELEMENT = "set";
public static final String MAP_ELEMENT = "map";
/**
* 该方法才是真正的解析ele过程,主要对constructor-arg element的解析,如value or ref
* @param ele constructor-arg element
* @param bd bean的定义载体
* @param defaultValueType vaule 的默认类型
* @return
*/
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
//这里为自定义元素的解析的过程
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//如果是bean元素
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
//通过方法parseBeanDefinitionElement来构建一个BeanDefinitionHolder实例对象
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
//
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//如果是ref元素
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
//获取ref属性
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.
//如果当前有其他bean的引用,获取parent属性
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;
}
//对ref进行封装
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;
}
}
上述方法是针对于不同的子元素的解析过程,由于太多了这里就不仔细深究其原理,感兴趣的可以自己去看,接着上面的分析往下看:
- 对于ref的属性通过构建RuntimeBeanReference对象并对其属性的封装
//通过ref属性来构建一个RuntimeBeanReference实例对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
//属性封装source
ref.setSource(extractSource(ele));
- 对于value属性是通过TypedStringValue对象并对其属性的封装
//通过获取到的value属性来构建一个TypedStringValue实例对象
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
//source属性的封装
valueHolder.setSource(extractSource(ele));
以上就是对constructor-arg字标签分析的整个过程
网友评论