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

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

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

一、PropertyValue

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

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

1.1 AttributeAccessor

  1. public interface AttributeAccessor {
  2. // 对属性进行增删改查
  3. void setAttribute(String name, @Nullable Object value);
  4. Object getAttribute(String name);
  5. Object removeAttribute(String name);
  6. boolean hasAttribute(String name);
  7. String[] attributeNames();
  8. }

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

1.2 BeanMetadataElement

  1. public interface BeanMetadataElement {
  2. Object getSource();
  3. }

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

  1. public class BeanMetadataAttribute implements BeanMetadataElement {
  2. // 属性 key - value
  3. private final String name;
  4. private final Object value;
  5. // 属性所属的对象
  6. private Object source;
  7. }

1.3 PropertyValue

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

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

直接设置 convertedValue 值时 converted = true。

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

二、PropertyValues

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

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

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

(1) 属性

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

(2) addPropertyValues

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

  1. public MutablePropertyValues addPropertyValues(@Nullable Map<?, ?> other) {
  2. if (other != null) {
  3. other.forEach((attrName, attrValue) -> addPropertyValue(
  4. new PropertyValue(attrName.toString(), attrValue)));
  5. }
  6. return this;
  7. }
  8. // 所有类型的属性键值对都会转换成 PropertyValue 后进行存储
  9. public MutablePropertyValues addPropertyValue(PropertyValue pv) {
  10. for (int i = 0; i < this.propertyValueList.size(); i++) {
  11. PropertyValue currentPv = this.propertyValueList.get(i);
  12. if (currentPv.getName().equals(pv.getName())) {
  13. pv = mergeIfRequired(pv, currentPv);
  14. setPropertyValueAt(pv, i);
  15. return this;
  16. }
  17. }
  18. this.propertyValueList.add(pv);
  19. return this;
  20. }

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

  1. private PropertyValue mergeIfRequired(PropertyValue newPv, PropertyValue currentPv) {
  2. Object value = newPv.getValue();
  3. if (value instanceof Mergeable) {
  4. Mergeable mergeable = (Mergeable) value;
  5. if (mergeable.isMergeEnabled()) {
  6. Object merged = mergeable.merge(currentPv.getValue());
  7. return new PropertyValue(newPv.getName(), merged);
  8. }
  9. }
  10. return newPv;
  11. }

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

  1. public List<E> merge(@Nullable Object parent) {
  2. List<E> merged = new ManagedList<>();
  3. merged.addAll((List<E>) parent);
  4. merged.addAll(this);
  5. return merged;
  6. }

(3) getPropertyValue

  1. public PropertyValue getPropertyValue(String propertyName) {
  2. for (PropertyValue pv : this.propertyValueList) {
  3. if (pv.getName().equals(propertyName)) {
  4. return pv;
  5. }
  6. }
  7. return null;
  8. }

(4) changesSince

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

  1. @Override
  2. public PropertyValues changesSince(PropertyValues old) {
  3. MutablePropertyValues changes = new MutablePropertyValues();
  4. // 1. 返回空集合
  5. if (old == this) {
  6. return changes;
  7. }
  8. // 2. 返回 propertyValueList 中有而 old 没有的元素集合
  9. for (PropertyValue newPv : this.propertyValueList) {
  10. PropertyValue pvOld = old.getPropertyValue(newPv.getName());
  11. if (pvOld == null || !pvOld.equals(newPv)) {
  12. changes.addPropertyValue(newPv);
  13. }
  14. }
  15. return changes;
  16. }

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

Spring 属性注入(四)属性键值对 - PropertyValue的更多相关文章

  1. Learning Spark 第四章——键值对处理

    本章主要介绍Spark如何处理键值对.K-V RDDs通常用于聚集操作,使用相同的key聚集或者对不同的RDD进行聚集.部分情况下,需要将spark中的数据记录转换为键值对然后进行聚集处理.我们也会对 ...

  2. Spring @Value注入static属性

    import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Com ...

  3. Spring 中注入 properties 中的值

    <bean id="ckcPlaceholderProperties" class="org.springframework.beans.factory.confi ...

  4. 解决Spring Boot 使用RedisTemplate 存储键值出现乱码 \xac\xed\x00\x05t\x00

    spring-data-redis的RedisTemplate<K, V>模板类在操作redis时默认使用JdkSerializationRedisSerializer来进行序列化解决方法 ...

  5. Spring 中IOC(控制反转)&& 通过SET方式为属性注入值 && Spring表达式

    ### 1. Spring IoC IoC:Inversion of control:控制反转:在传统开发模式下,对象的创建过程和管理过程都是由开发者通过Java程序来实现的,操作权在开发者的Java ...

  6. 六 Spring属性注入的四种方式:set方法、构造方法、P名称空间、SPEL表达式

    Spring的属性注入: 构造方法的属性注入 set方法的属性注入

  7. Spring中对象和属性的注入方式

    一:Spring的bean管理 1.xml方式 bean实例化三种xml方式实现 第一种 使用类的无参数构造创建,首先类中得有无参构造器(重点) 第二种 使用静态工厂创建 (1)创建静态的方法,返回类 ...

  8. Java Spring-注解进行属性注入

    2017-11-06 21:19:43 一.Spring的注解装配BeanSpring2.5 引入使用注解去定义Bean @Component 描述Spring框架中Bean Spring的框架中提供 ...

  9. .NET领域最为流行的IOC框架之一Autofac WebAPI2使用Autofac实现IOC属性注入完美解决方案 AutoFac容器初步

    .NET领域最为流行的IOC框架之一Autofac   一.前言 Autofac是.NET领域最为流行的IOC框架之一,微软的Orchad开源程序使用的就是Autofac,Nopcommerce开源程 ...

随机推荐

  1. opencv矩阵操作

    1.初始化矩阵: 方式一.逐点赋值式: CvMat* mat = cvCreateMat( 2, 2, CV_64FC1 ); cvZero( mat ); cvmSet( mat, 0, 0, 1 ...

  2. flume 使用手册

    以下jie皆来自官网: 1:首先版本是flume 1.8 查看版本:  bin/flume-ng version 2:配置与启动 https://flume.apache.org/FlumeUserG ...

  3. Centos7 安装 erlang rabbitmq

    1.安装Erlang依赖采用官网的rpm包的形式进行安装,不采用yum(由系统进行自动安装 可能因为版本低的问题而出现一系列问题) erlang依赖 rpm包下载地址https://github.co ...

  4. 一个查询指定错误记录数据表错误记录条数的shell脚本

    #!/bin/bash #author:skycheng #parameters db_user=dbuser db_pass=dbpass db_host=xxx.xxx.xxx.xxx datab ...

  5. 使用in ()进行批量删除

    public bool DeleteList(string idlist ) { StringBuilder strSql=new StringBuilder(); strSql.Append(&qu ...

  6. java.net.UnknownHostException: www.terracotta.org

    异常日志: java.net.UnknownHostException: www.terracotta.org at java.net.PlainSocketImpl.connect(PlainSoc ...

  7. Excel图表编辑---表格移动,样式修改

    一.移动位置和调整大小 先鼠标选中如下面这个图片,之后点击上方的设计按钮,随后选择右边的, 再选择,就可以实现图片的表格之间的移动. 其中移动图表里面的,选中这个之后,图表的大小会根据窗口的大小自动调 ...

  8. C# winform 支持html5的 控件

    OpenWebKitSharp WebKit.net c#winform中使用WebKit传递js对象实现与网页交互 分类: .NET开发2013-08-18 23:55 2496人阅读 评论(1)  ...

  9. java面试题:基础知识

    类和对象 Q:讲一下面向对象OOP思想. 面向对象主要是抽象,封装,继承,多态. 多态又分为重载和重写.重载主要是方法参数类型不一样,重写则是方法内容不一样. Q:抽象类和接口有什么区别? 抽象类中可 ...

  10. JavaWeb网站后台开发记录手册

    1.javaweb网站后台开发 1.封装DBTools类 1.注册数据库驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); 2 ...