• Spring 属性注入(四)属性键值对


    Spring 属性注入(四)属性键值对 - PropertyValue

    Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)

    PropertyValue 和 PropertyValues 都位于 org.springframework.beans 包下,是 bean 属性键值对的封装,缓存了对 key-value 解析相关的信息,避免重复解析。

    一、PropertyValue

    PropertyValue 类图

    • AttributeAccessor 可以访问对象的属性或将属性附加到对象上。
    • BeanMetadataElement 持有属性信息的对象。
    • BeanMetadataAttributeAccessor 实现了 AttributeAccessor 和 BeanMetadataElement 两个接口,属性为 BeanMetadataAttribute 对象。
    • PropertyValue 属性键值对。

    有一点不太明白 PropertyValue 表示一个属性键值对,PropertyValues 表示多个属性键值对,那 PropertyValue 为什么还要继承 AttributeAccessor 接口,AttributeAccessor 可以对多个属性进行管理。

    1.1 AttributeAccessor

    public interface AttributeAccessor {
        // 对属性进行增删改查
        void setAttribute(String name, @Nullable Object value);
        Object getAttribute(String name);
        Object removeAttribute(String name);
        boolean hasAttribute(String name);
    
        String[] attributeNames();
    }
    

    AttributeAccessor 接口的实现类为 AttributeAccessorSupport,在这个类中维护一个 Map<String, Object> attributes = new LinkedHashMap<>(),对属性进行增删改查。

    1.2 BeanMetadataElement

    public interface BeanMetadataElement {
        Object getSource();
    }
    

    BeanMetadataAttributeAccessor 的实现了 AttributeAccessor 和 BeanMetadataElement 两个接口,内部维护的是 <propertyName, BeanMetadataAttribute> 键值对,BeanMetadataAttribute 是一个持有属性对和源的对象。

    public class BeanMetadataAttribute implements BeanMetadataElement {
        // 属性 key - value
        private final String name;
        private final Object value;
        
        // 属性所属的对象
        private Object source;
    }
    

    1.3 PropertyValue

    PropertyValue 缓存了对 key-value 解析相关的信息,避免重复解析。如 AbstractNestablePropertyAccessor#processLocalProperty 进行属性注入时,会判断是否需要对属性值进行类型转换。相关字段如下:

    // 1.1 属性名称
    private final String name;
    // 1.1 属性值
    private final Object value;
    
    // 2.1 属性值是否为 Optional
    private boolean optional = false;
    // 2.2 属性值是否已经进行了类型转换
    private boolean converted = false;
    // 2.3 类型转换后的属性值
    private Object convertedValue;
    
    // 3.1 属性值是否需要进行类型转换,如果转换前后对象都是同一个,说明不用转换
    volatile Boolean conversionNecessary;
    // 3.2 缓存解析后的属性名称,如 attr['info']['name']
    transient volatile Object resolvedTokens;
    

    直接设置 convertedValue 值时 converted = true。

    public synchronized void setConvertedValue(@Nullable Object value) {
        this.converted = true;
        this.convertedValue = value;
    }
    

    二、PropertyValues

    PropertyValues 类图

    PropertyValues 表示多个属性键值对,接口如下:

    public interface PropertyValues extends Iterable<PropertyValue> {
        // @since 5.1
        default Iterator<PropertyValue> iterator() {
            return Arrays.asList(getPropertyValues()).iterator();
        }
    
        PropertyValue[] getPropertyValues();
        PropertyValue getPropertyValue(String propertyName);
    
        boolean contains(String propertyName);
        boolean isEmpty();
       
        // old 和当前的对比,返回当前有而 old 没有的元素
        PropertyValues changesSince(PropertyValues old);
    }
    

    PropertyValues 的默认实现为 MutablePropertyValues,内部也是维护了一个 List<PropertyValue> propertyValueList 集合。MutablePropertyValues 会将属性值转换成 PropertyValue 进行存储。

    (1) 属性

    private final List<PropertyValue> propertyValueList;
    
    // 已经解析过的 PropertyValue
    private Set<String> processedProperties;
    private volatile boolean converted = false;
    

    (2) addPropertyValues

    可以将 Map、PropertyValue 属性添加到集合中,MutablePropertyValues 统一封装成 PropertyValue 进行存储。

    public MutablePropertyValues addPropertyValues(@Nullable Map<?, ?> other) {
        if (other != null) {
            other.forEach((attrName, attrValue) -> addPropertyValue(
                    new PropertyValue(attrName.toString(), attrValue)));
        }
        return this;
    }
    
    // 所有类型的属性键值对都会转换成 PropertyValue 后进行存储
    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;
    }
    

    注意如果属性值是 Mergeable 类型会先合并,代码如下:

    private PropertyValue mergeIfRequired(PropertyValue newPv, PropertyValue currentPv) {
        Object value = newPv.getValue();
        if (value instanceof Mergeable) {
            Mergeable mergeable = (Mergeable) value;
            if (mergeable.isMergeEnabled()) {
                Object merged = mergeable.merge(currentPv.getValue());
                return new PropertyValue(newPv.getName(), merged);
            }
        }
        return newPv;
    }
    

    Mergeable 的子类有 ManagedSet、ManagedList、ManagedMap、ManagedProperties、ManagedArray,合并也很简单,以 ManagedList 为例:

    public List<E> merge(@Nullable Object parent) {
        List<E> merged = new ManagedList<>();
        merged.addAll((List<E>) parent);
        merged.addAll(this);
        return merged;
    }
    

    (3) getPropertyValue

    public PropertyValue getPropertyValue(String propertyName) {
        for (PropertyValue pv : this.propertyValueList) {
            if (pv.getName().equals(propertyName)) {
                return pv;
            }
        }
        return null;
    }
    

    (4) changesSince

    查找相对 old 发生变化的所有元素。

    @Override
    public PropertyValues changesSince(PropertyValues old) {
        MutablePropertyValues changes = new MutablePropertyValues();
        // 1. 返回空集合
        if (old == this) {
            return changes;
        }
    
        // 2. 返回 propertyValueList 中有而 old 没有的元素集合
        for (PropertyValue newPv : this.propertyValueList) {
            PropertyValue pvOld = old.getPropertyValue(newPv.getName());
            if (pvOld == null || !pvOld.equals(newPv)) {
                changes.addPropertyValue(newPv);
            }
        }
        return changes;
    }
    

    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    用javascript实现html页面之间的参数传递
    个性的研究
    [luogu1967] 货车运输
    mscorwks.dll 没有被指定在Windows上运行,错误状态 0xc000012f
    dom4j解析xml时报:系统找不到指定路径
    The strip's most beautiful gril,rendezvous never a cover;
    无法在自定义编辑器中开发TransactSQL文件
    Default code generation is disabled for model。。。
    使用IE9艰辛历程之从什么网页都打不开到什么都秒开的传奇
    Metro IE10快捷方式 不小心删了
  • 原文地址:https://www.cnblogs.com/binarylei/p/10278556.html
Copyright © 2020-2023  润新知