美文网首页
spring容器标签解析之constructor-arg

spring容器标签解析之constructor-arg

作者: 会上树的程序猿 | 来源:发表于2019-06-19 20:22 被阅读0次

上节说了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());

}

再来看结果

微信截图_20190617211237.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字标签分析的整个过程

相关文章

网友评论

      本文标题:spring容器标签解析之constructor-arg

      本文链接:https://www.haomeiwen.com/subject/mhzzfctx.html