【你好Archaius】十:Netflix Archaius集合数据类型的动态属性Property


每日一句:

认真做事,只能把事情做对;用心做事,才能把事情做好!

前言

前面了解了PropertyWrapper下面的一些基础类型的动态属性。如:DynamicStringProperty,DynamicBooleanProperty,DynamicDoubleProperty等。前面说过 DynamicPropertySupport只有两个方法

  • getString(String propName):获取一个字符串配置值
  • addConfigurationListener(PropertyListener expandedPropertyListener):想底层配置Configuration 中添加一个属性监听器

属性的动态性都是最终由DynamicPropertySupport提供的监听器完成。而动态属性的值都是由DynamicPropertySupport.getString 方法来得到 并最终被 动态属性包装器(PropertyWrapper) 转换成各种类型,这些类型都是基本类型(这里把string也当作配置的基本类型吧)。这一篇文章我们就来讨论其他的一些常用的数据类型。

继承关系

在这里插入图片描述
我们红框圈出来是这篇文章要说的,类很多 但是都很简单!说白了都是在PropertyWrapper的基础上来做了一层类型转换而已!

DynamicListProperty & DynamicStringListProperty

demo

config.properties 配置文件中配置:

sports=basketball,football,ping-pong
student=name=zhangsan,age=30,address=guangzhou

示例代码

   DynamicStringListProperty listProperty = new DynamicStringListProperty("sports", "");
    System.out.println(listProperty.get());
    DynamicStringMapProperty mapProperty = new DynamicStringMapProperty("student", "");
    System.out.println(mapProperty.get());

输出:

[basketball, football, ping-pong]
[name=zhangsan, age=30, address=guangzhou]

DynamicListProperty

动态列表属性的父类 直接继承Property。注意和PropertyWrapper没有关系哦!如果我们有需要自定义实现的列表类型 可以实现该抽象类。

public abstract class DynamicListProperty<T> implements Property<List<T>> {
//缓存的列表值
    private volatile List<T> values;
    //默认值
    private List<T> defaultValues;
    //string类型是基础 复杂类型的做法就是 先获取配置文件中的字符串形式 再转换成复杂类型
    private DynamicStringProperty delegate;
    //分割器
    private Splitter splitter;
    //默认的列表分隔符
    public static final String DEFAULT_DELIMITER = ",";
    public DynamicListProperty(String propName, List<T> defaultValue, String delimiterRegex) {
        setup(propName, defaultValue, delimiterRegex);
    }
   
   //这个setUp方法主要是在构造一个分割器Splitter
    private void setup(String propName, List<T> defaultValue, String delimiterRegex) {
        setup(propName, defaultValue, Splitter.onPattern(delimiterRegex).omitEmptyStrings().trimResults());
    }
    //这里的setup是核心方法
    private void setup(String propName, List<T> defaultValue, Splitter splitter) {
        this.defaultValues = (defaultValue == null ? null : 
            Collections.unmodifiableList(new ArrayList<T>(defaultValue)));
        this.splitter = splitter;
      //还是通过  DynamicPropertyFactory获取一个DynamicStringProperty 
        delegate = DynamicPropertyFactory.getInstance().getStringProperty(propName, null);
        //加载并将string转成list
        load();
        Runnable callback = new Runnable() {
            @Override
            public void run() {
                propertyChangedInternal();
            }
        };
        //添加回调 默认propertyChangedInternal是空实现 
        //注意这里没有像PropertyWrapper那样处理 空实现的不注册 以提高性能
        delegate.addCallback(callback);
    }
    //将string转换成list的过程
    protected List<T> transform(List<String> stringValues) {
        List<T> list = new ArrayList<T>(stringValues.size());
        for (String s : stringValues) {
            list.add(from(s));
        }
        return Collections.unmodifiableList(list);    
    }
    protected void load() {
        if (delegate.get() == null) {
            values = defaultValues;
        } else {
            values = transform(split(delegate.get()));
        }
    }
    //这个是留给子类唯一的一个方法。将string转成子类想要的类型
    protected abstract T from(String value);
}
  • 处理列表动态属性 还是要先使用DynamicPropertyFactory.getInstance().getStringProperty(key) 获取一个字符串类型的动态属性。然后再把字符串转换成对应的类型
  • 子类实现DynamicListProperty只需要 实现from方法 将字符串值转成对应的类型T
  • 分隔符是可以通过构造器指定的

好了分析到这 其实逻辑已经清楚了DynamicStringListProperty 这个类只是实现了from方法,并且什么事都没干 把原本的值返回回去就好了。

   @Override
    protected String from(String value) {
        return value;
    }

注意:这个value不是整个配置项的value 而是经过拆分之后的子value。

DynamicMapProperty & DynamicStringMapProperty

分析完DynamicListProperty 我们再看DynamicMapProperty就简单了。DynamicMapProperty继承自DynamicStringMapProperty 所以可以几乎可以猜到它的逻辑就是把List ⇒ Map<K,T> 的过程!

public abstract class DynamicMapProperty<TKEY, TVAL> extends DynamicStringListProperty {
//默认值
    private Map<TKEY,TVAL> defaultValuesMap;
//配置的值
    private volatile Map<TKEY,TVAL> values;
    //为了节约篇幅 我直接把其他重载的构造方法删掉
    public DynamicMapProperty(String propName, String defaultValue) {
    //这一句执行完了 我们就能得到一个list  这个list可以通过getDefaultValue()获取
        super(propName, defaultValue);
        //下面这一句就是 将list 转成map的过程
        defaultValuesMap = parseMapFromStringList(getDefaultValue());
    }
  //这个方法可以说是整个类最核心的一个方法了 没有之一
  //转换过程很简单 就是字符串的split 根据等号分隔符
  //分割完之后 分别调用了 getKey  getValue方法获取key和value  而这两个方法是抽象方法 需要子类实现
  //因为 split之后 key和value都是字符串类型 如果还需要在基础上扩展 可以重写这两个方法
    protected Map<TKEY,TVAL> parseMapFromStringList(List<String> strings) {
        if (strings == null || strings.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<TKEY,TVAL> map = new LinkedHashMap<TKEY,TVAL>(strings.size());
        for (String s : strings) {
            String kv[] = getKeyValue(s);
            if (kv.length == 2) {
                map.put(getKey(kv[0]), getValue(kv[1]));
            } else {
                logger.warn("Ignoring illegal key value pair: " + s);
            }
        }
        return Collections.unmodifiableMap(map);
    }
    
    //这里的分隔符是定死的 无法再指定 必须是等号
    protected String[] getKeyValue(String keyValue) {
        return keyValue.split("=");    
    }
    //下面两个方法需要子类提供实现 可以返回指定的类型
    protected abstract TKEY getKey(String key);
    protected abstract TVAL getValue(String value);    
}

看了上面的代码 其实和我们的思路是一样的。就是把list转成map的过程。
那DynamicStringMapProperty一定是实现了getKey和getValue 并且什么事没做 只是返回参数中的 key和value。

    @Override
    protected String getKey(String key) {
        return key;
    }

    @Override
    protected String getValue(String value) {
        return value;
    }   

DynamicSetProperty & DynamicStringSetProperty

好了 set数据类型 那肯定是一毛一样的处理啊。这里就不再赘述了。

DerivedStringProperty & StringDerivedProperty

没错 你没看错 DerivedStringPropertyStringDerivedProperty
它两都是对字符串转换成复杂类型做得支持。 先不吐槽了吧、、木已成舟 吐槽也于事无补 我们搞清楚两者的差异 择优吧。

但是两者有一些区别
DerivedStringProperty:

  • 是抽象类 不能直接使用
  • 需要实现derive方法完成类型转换
  • 直接实现Property
  • 没有对propertyChanged做实现
    StringDerivedProperty:
  • 是具体实现类 可以直接使用
  • 继承PropertyWrapper
  • 对propertyChanged做了实现

基于DerivedStringProperty实现复杂类型转换

public class DynamicPersonProperty  extends DerivedStringProperty<DynamicPersonProperty.Person> {
  public DynamicPersonProperty(String proName , String defalutName){
    super(proName , defalutName);
  }
  //只需要实现这个方法 转换类型
  @Override
  protected Person derive(String value) {
    if (StringUtils.isBlank(value)){
      return null;
    }else{
      Person person = new Person();
      String[] split = value.split(",");
      person.setName(split[0].split("=")[1]);
      person.setAge(split[1].split("=")[1]);
      return person;
    }
  }
  @Getter
  @Setter
  public static class Person{
    private String name;
    private String age;
    @Override
    public String toString() {
      return "Person{" +
                 "name='" + name + '\'' +
                 ", age='" + age + '\'' +
                 '}';
    }
  }
}
//测试 
  public static void main(String[] args) {
    DynamicPersonProperty personProperty = new DynamicPersonProperty("student" , null);
    System.out.println(personProperty.get());
  }

//输出
Person{name='zhangsan', age='30'}

基于StringDerivedProperty实现复杂类型

   StringDerivedProperty<Person> student = new StringDerivedProperty<Person>("student", null,(str) ->{
      Person person = new Person();
      String[] split = str.split(",");
      person.setName(split[0].split("=")[1]);
      person.setAge(split[1].split("=")[1]);
      return person;
    });
    System.out.println(student.getValue());

也能实现 string向复杂类型的转换。还简单了很多。但是 但是 需要引入guava包。因为编译依赖guava的Function。Archaius导入Guava包的maven Type是Runtime 所以如果不导入Guava包会报编译错误!

Archaius项目 两处依赖了Guava包 DefaultContextualPredicate和StringDerivedProperty。
*** 实例开发中选择哪一个 看自己的心情吧 如果项目中已经有了Guava包 用第二个很合适!其实这两个我个人觉得没有都存在的必要!***

ChainLink

提供动态属性连接功能 我们还是先来看一个官方给的例子

//获取一个动态的string类型的属性
DynamicStringProperty pString = DynamicPropertyFactory.getInstance()
                                        .getStringProperty("defaultString", "default-default");
//这行代码可以简单的理解 动态属性pString 作为   overrideString的默认属性 如果overrideString没有值得时候 会得到pString的值
//获取值得顺序应该是 overrideString配置的值->pString配置的值 ->pString默认值                                  
    ChainedDynamicProperty.StringProperty fString = new ChainedDynamicProperty.StringProperty("overrideString", pString);
    // 查找流程:overrideString配置的值 为空 那就找pString配置的值 pString配置的值也为空 接着找
    //pString默认值  pString默认值是 “default-default”
    assertTrue("default-default".equals(fString.get()));
    //pString的值被设置成 default
    ConfigurationManager.getConfigInstance().setProperty("defaultString", "default");
    //查找流程:overrideString配置的值 为空 那就找pString的值 上一行代码把pString配置的值设置成default  查找结束
    assertTrue("default".equals(fString.get()));
    //设置overrideString的值
    ConfigurationManager.getConfigInstance().setProperty("overrideString", "override");
    //查找流程 overrideString配置的值 为override 有值 直接返回  清楚了这个流程 下面的代码就不解释了
    assertTrue("override".equals(fString.get()));
    
    ConfigurationManager.getConfigInstance().clearProperty("overrideString");
    assertTrue("default".equals(fString.get()));
    ConfigurationManager.getConfigInstance().clearProperty("defaultString");
    assertTrue("default-default".equals(fString.get()));

上面的注释很明白 == ChainedDynamicProperty.StringProperty fString = new ChainedDynamicProperty.StringProperty(“overrideString”, pString);== 就这行代码而言,系统会先查找“overrideString”对应的动态属性 DynamicStringProperty 然后将pString设置成overrideString下一个节点。
所以整个查找流程:
overrideString配置的值---->pString的值 ---->pString默认值

篇幅问题 就不对源码进行追踪了。感兴趣的同学可以下去翻阅一下。

本文把PropertyWapper以外的Property介绍了一遍。平时工作中 应该使用的不是很多。因为DynamicStringProperty可以搞定一切。但是了解了这些 你会使用的更优雅。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值