目录
每日一句:
认真做事,只能把事情做对;用心做事,才能把事情做好!
前言
前面了解了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
没错 你没看错 DerivedStringProperty 和 StringDerivedProperty
它两都是对字符串转换成复杂类型做得支持。 先不吐槽了吧、、木已成舟 吐槽也于事无补 我们搞清楚两者的差异 择优吧。
但是两者有一些区别
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可以搞定一切。但是了解了这些 你会使用的更优雅。