美文网首页
spring容器标签解析之property

spring容器标签解析之property

作者: 会上树的程序猿 | 来源:发表于2019-06-19 22:59 被阅读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();
    
    • 遍历获取property元素
    • 调用parsePropertyElement方法进行真正的解析过程
    • 首先获取name属性
    public static final String NAME_ATTRIBUTE = "name";
    String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
    
    • 通过name属性构建PropertyEntry实体对象,接着调用ParseState#push保存该实例
    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);
    }
    
    • 解析value属性
    /**
     * 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;
        }
    }
    
    • 通过解析之后的value的属性值去构建PropertyValue实例
    PropertyValue pv = new PropertyValue(propertyName, val);
    
    • 封装PropertyValue属性
    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);
    
    • 删除PropertyEntry实例
    ''''''
    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标签解析的过程

    相关文章

      网友评论

          本文标题:spring容器标签解析之property

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