spring容器标签解析之property

2019-06-19  本文已影响0人  会上树的程序猿

上节我们了解了spring对constructor-arg字标签的解析过程,可能在实际的开发中这种使用的场景很少,但至少我们知道了是如何处理的过程,接下来我们来了解下spring对property字标签的解析过程,首先我们来看这段代码:

<?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">
  <property name="name" value="无敌"/>
  <property name="age" value="20"/>
</bean>
</beans>

这段代码很简单,其中我们定义的Student的属性在配置文件中是用<property>来包裹,这里不多说了,直接来看解析的过程:

/**
 * Parse property sub-elements of the given bean element.
 */
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
    //获取所有的子元素
    NodeList nl = beanEle.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        //元素是property的
        if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
            //真正的解析方法
            parsePropertyElement((Element) node, bd);
        }
    }
}



/**
 * 解析property的过程
 * @param ele property元素
 * @param bd
 */
public void parsePropertyElement(Element ele, BeanDefinition bd) {
    //获取name属性
    String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
    //name的属性为null时,报以下错误
    if (!StringUtils.hasLength(propertyName)) {
        error("Tag 'property' must have a 'name' attribute", ele);
        return;
    }
    //1.通过获取到的name属性构建PropertyEntry实例对象
    //1.1.调用ParseState#push方法将实例保存
    this.parseState.push(new PropertyEntry(propertyName));
    try {
        //不允许对同一个property的属性进行多次操作
        if (bd.getPropertyValues().contains(propertyName)) {
            error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
            return;
        }
        //解析value属性
        Object val = parsePropertyValue(ele, bd, propertyName);
        //通过解析之后的value的属性值去构建PropertyValue实例
        PropertyValue pv = new PropertyValue(propertyName, val);
        parseMetaElements(ele, pv);
        //封装PropertyValue属性
        pv.setSource(extractSource(ele));
        bd.getPropertyValues().addPropertyValue(pv);
    }
    finally {
        //删除PropertyEntry实例
        this.parseState.pop();
    }
}

上述是spring解析property的过程,简单的分析总结下每一步的过程:

NodeList nl = beanEle.getChildNodes();
public static final String NAME_ATTRIBUTE = "name";
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
PropertyEntry.java

private final String name;
/**
 * Creates a new instance of the {@link PropertyEntry} class.
 * @param name the name of the JavaBean property represented by this instance
 * @throws IllegalArgumentException if the supplied {@code name} is {@code null}
 * or consists wholly of whitespace
 */
public PropertyEntry(String name) {
    if (!StringUtils.hasText(name)) {
        throw new IllegalArgumentException("Invalid property name '" + name + "'.");
    }
    this.name = name;
}

 第 2步:
this.parseState.push(new PropertyEntry(propertyName));

ParserState.java
/**
 * Add a new {@link Entry} to the {@link LinkedList}.
 */
public void push(Entry entry) {
    this.state.push(entry);
}
/**
 * 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;
    }
}
PropertyValue pv = new PropertyValue(propertyName, val);
pv.setSource(extractSource(ele));

MutablePropertyValues.java
private final List<PropertyValue> propertyValueList;

第2步:
/**
 * Add a PropertyValue object, replacing any existing one for the
 * corresponding property or getting merged with it (if applicable).
 * @param pv the PropertyValue object to add
 * @return this in order to allow for adding multiple property values in a chain
 */
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
    for (int i = 0; i < this.propertyValueList.size(); i++) {
        PropertyValue currentPv = this.propertyValueList.get(i);
        //如果相等,合并
        if (currentPv.getName().equals(pv.getName())) {

            pv = mergeIfRequired(pv, currentPv);
            //覆盖当前的值
            setPropertyValueAt(pv, i);
            return this;
        }
    }
    //将新的值保存
    this.propertyValueList.add(pv);
    return this;
}
第3步:合并过程

/**
 * Merges the value of the supplied 'new' {@link PropertyValue} with that of
 * the current {@link PropertyValue} if merging is supported and enabled.
 * @see Mergeable
 */
private PropertyValue mergeIfRequired(PropertyValue newPv, PropertyValue currentPv) {
    //获取value属性的值
    Object value = newPv.getValue();
    if (value instanceof Mergeable) {
        Mergeable mergeable = (Mergeable) value;
        //判断value是否启动了合并功能,是的话返回true反之false
        if (mergeable.isMergeEnabled()) {
            //合并
            Object merged = mergeable.merge(currentPv.getValue());
            //利用合并后的merged来构建PropertyValue实例对象
            return new PropertyValue(newPv.getName(), merged);
        }
    }
    return newPv;
}

第4步:Mergeable接口
public interface Mergeable {

/**是否为当前实例开启合并*/
boolean isMergeEnabled();

/**
 * Merge the current value set with that of the supplied object.
 * <p>The supplied object is considered the parent, and values in
 * the callee's value set must override those of the supplied object.
 * @param parent the object to merge with
 * @return the result of the merge operation
 * @throws IllegalArgumentException if the supplied parent is {@code null}
 * @throws IllegalStateException if merging is not enabled for this instance
 * (i.e. {@code mergeEnabled} equals {@code false}).
 */
Object merge(@Nullable Object parent);
''''''
ParserState.java
/**
 * Internal {@link LinkedList} storage.
 */
private final LinkedList<Entry> state;
第1步:调用ParserState#pop方法
this.parseState.pop();

/**
 * Remove an {@link Entry} from the {@link LinkedList}.
 */
public void pop() {
    this.state.pop();
}

上述就是整个property标签解析的过程

上一篇下一篇

猜你喜欢

热点阅读