每日一句
没有礁石,就没有美丽的浪花;没有挫折,就没有壮丽的人生
我们接着上一篇继续聊,上一篇讲的是应用 这一篇我们讲原理 讲如何实现。在阅读本文之前 希望你已对Archaius有所了解
属性加载
IClientConfig coredy1 = DefaultClientConfigImpl.getClientConfigWithDefaultValues("coredy1");
IClientConfig coredy = DefaultClientConfigImpl.getClientConfigWithDefaultValues("coredy");
System.out.println(coredy.get(IClientConfigKey.Keys.ConnectTimeout));
System.out.println(coredy1.get(IClientConfigKey.Keys.ConnectTimeout));
还是把上一篇的例子拿过来,我们跟进getClientConfigWithDefaultValues方法。
public static DefaultClientConfigImpl getClientConfigWithDefaultValues(String clientName) {
//这里使用的是默认的name_space
return getClientConfigWithDefaultValues(clientName, DEFAULT_PROPERTY_NAME_SPACE);
}
继续跟进getClientConfigWithDefaultValues
public static DefaultClientConfigImpl getClientConfigWithDefaultValues(String clientName, String nameSpace) {
DefaultClientConfigImpl config = new DefaultClientConfigImpl(nameSpace);
config.loadProperties(clientName);
return config;
}
上面方法做了两件事情 一:实例化一个DefaultClientConfigImpl对象 我们说过 DefaultClientConfigImpl是IClientConfig的唯一实现。第二:调用IClientConfig这个接口的loadProperties方法。重点看一下loadProperties方法
public void loadProperties(String restClientName) {
//启用动态属性 如果开启属性将具有动态 性 这里也是唯一一处将该值设置成true的地方
//所以如果仅仅是默认值 不支持动态属性的
enableDynamicProperties = true;
//设置clientName
setClientName(restClientName);
//加载默认属性
loadDefaultValues();
//这里可以看到底层的配置还是通过 Archaius来配置的 所以我们把配置写在classpath的config.properties中是生效的
//对于subset这个方法 举个例子可能会更清楚 例如我们在config文件中的配置是 coredy.ribbon.ReadTimeout
//我们调用subset(”coredy“) 那就会给我们返回所有以coredy开头的Configuration
Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName);
//遍历得到的Configuration 将属性放到全局的配置里面properties
for (Iterator<String> keys = props.getKeys(); keys.hasNext(); ) {
String key = keys.next();
String prop = key;
try {
if (prop.startsWith(getNameSpace())) {
prop = prop.substring(getNameSpace().length() + 1);
}
//特别注意这里是使用getStringValue(props, key)来获取值得
//意为着 你配置的属性是a,b,c 那么最终在全局配置properties里面的值也是a,b,c
setPropertyInternal(prop, getStringValue(props, key));
} catch (Exception ex) {
throw new RuntimeException(String.format("Property %s is invalid", prop));
}
}
}
- 只有调用这个loadProperties方法 才会把enableDynamicProperties 这个属性设置成true 所以只有默认属性的时候 是不具有动态性的
- loadDefaultValues() 所以在我们通过DefaultClientConfigImpl静态方法获取实例的时候 默认是要加载默认配置的
- 加载客户端属性 (通过subset(restClientName)获取配置 然后放在全局配置) 的时候使用的是getStringValue() 这个方法 它保证了你在配置文件配置的是什么样子 那么加载出来的就是什么样子(注意和下面我们要说的加载默认配置的时候的区别)
继续跟进setPropertyInternal 设置客户端属性 (我们把非默认属性统一称为客户端属性)
String stringValue = (value == null) ? "" : String.valueOf(value);
//直接将客户端属性放入全局属性中
properties.put(propName, stringValue);
//判断是否开启了动态属性 这里是开启了的
if (!enableDynamicProperties) {
return;
}
/*这里得到一个key值 这个key就会根据我们设置的namespace和clientName来决定
* coredy.ribbon.ReadTimeout or ribbon.ReadTimeout 客户端配置的key和默认key
* (clientName == null) ? getDefaultPropName(propName) : getInstancePropName(clientName, propName);
**/
String configKey = getConfigKey(propName);
//获取动态属性 注册回调函数
final DynamicStringProperty prop = DynamicPropertyFactory.getInstance().getStringProperty(configKey, null);
//属性更改会 执行回调 更改全局配置properties里面对应的值
Runnable callback = new Runnable() {
@Override
public void run() {
String value = prop.get();
if (value != null) {
properties.put(propName, value);
} else {
properties.remove(propName);
}
}
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (getClass() == other.getClass()) {
return toString().equals(other.toString());
}
return false;
}
@Override
public String toString() {
return propName;
}
@Override
public int hashCode() {
return propName.hashCode();
}
};
//属性更改会回调 callback
prop.addCallback(callback);
dynamicProperties.put(propName, prop);
上面的setPropertyInternal 主要是在处理属性的动态性。如果enableDynamicProperties为false直接返回了。到这里我们的客户端属性就加载进了全局配置中 并且所有的属性都具有动态性。那我们看一下如何加载默认属性的,我们跟进loadDefaultValues方法
//loadDefaultValues方法就不贴出来了 主要是 穷举所有的key 调用下面这个方法
putDefaultStringProperty(CommonClientConfigKey.ListOfServers, "");
跟进putDefaultStringProperty
String value = ConfigurationManager.getConfigInstance().getString(
getDefaultPropName(propName), defaultValue);
setPropertyInternal(propName, value);
代码很简单 就是获取默认的key值 然后依赖Archaius获取对应key的配置值。但是 但是有一点需要特别注意。这里的 ConfigurationManager.getConfigInstance().getString()方法获取的配置和刚刚上面我们说的getStringValue() 是有区别的。例如我们配置的值是a,b,c 那么getStringValue获取的值是a,b,c 而 getString()获取的却是 a! 所以默认值字符串类型的 配置里面出现逗号那就有问题了。
获取属性
上面我们把属性加载的整个流程跟进了一遍,那下面我们来看一下ribbon是怎么获取属性的。以getPropertyAsString为例 其他的几个方法getPropertyAsBoolean 、getPropertyAsInteger甚至get方法都是一样的。都是依赖getProperty方法进行查找。
IClientConfig clientConfig = DefaultClientConfigImpl.getClientConfigWithDefaultValues("coredy");
String propertyAsString = clientConfig.getPropertyAsString(new CommonClientConfigKey<String>("spring") {
}, null);
Assert.assertThat(propertyAsString, is("121212"));
上面是一个获取spring属性的例子。我们在实例化一个key的时候 CommonClientConfigKey 泛型是必须要写的。否则会出现IllegalArgumentException异常。 在构造参数中有一个 checkArgument(superclass instanceof ParameterizedType,
“%s isn’t parameterized”, superclass); 这样的判断 如果父类不是参数化泛型的实例 就会出异常。
我们继续跟进getPropertyAsString
//可以看到这个方法什么事情都么有做 只是做了一个类型的转换 获取的逻辑全部委托给了getProperty方法了
//其他的几个方法也是类似
public String getPropertyAsString(IClientConfigKey key, String defaultValue) {
Object rawValue = getProperty(key);
if (rawValue != null) {
return String.valueOf(rawValue);
}
return defaultValue;
}
//最终会执行该方法获取属性值
protected Object getProperty(String key) {
//如果开启动态属性值
if (enableDynamicProperties) {
String dynamicValue = null;
//先从动态属性缓存里面去找
DynamicStringProperty dynamicProperty = dynamicProperties.get(key);
if (dynamicProperty != null) {
dynamicValue = dynamicProperty.get();
}
//这里有一个小小的细节 为什么要这样写? 还要判断一次 只要动态属性开启 正常情况下动态属性缓存里面基本是有所有的属性的(我们通过属性加载流程可以看到)
//那为什么还要从新load一次? 想来想去 有一种情况可能要考虑 我们要获取的key不存在全局配置里面 并且是在我们IClientConfig加载完了之后
//我们加到配置文件中去的 这种情况下 单纯的 dynamicProperties.get(key);是获取不到 并且兜底的 properties.get(key);也获取不到
if (dynamicValue == null) {
//如果找不到 先去客户端配置
dynamicValue = DynamicProperty.getInstance(getConfigKey(key)).getString();
if (dynamicValue == null) {
//如果找不到 找默认配置
dynamicValue = DynamicProperty.getInstance(getDefaultPropName(key)).getString();
}
}
if (dynamicValue != null) {
return dynamicValue;
}
}
//如果都找不到 或者没有开启动态配置 那么从全局配置properties去找
return properties.get(key);
}
在动态属性开启的情况下 有一点细节可能要注意 作者不仅dynamicProperties.get(key);获取属性 在获取不到的情况下 又使用Archaius
的 DynamicProperty.getInstance(getConfigKey(key))去获取了一次 Archaius中的配置。并且是先加载客户端配置 再加载默认配置。这样就避免漏了 后续我们往Archaius中加的配置加载不到的问题。
设置属性
设置属性就非常简单了.直接设置到全局属性里面。
public <T> DefaultClientConfigImpl set(IClientConfigKey<T> key, T value) {
properties.put(key.key(), value);
return this;
}
这里需要想一下为什么没有想属性加载那么麻烦 考虑属性动态改变的问题。这里是因为属性动态改变是Archauis提供的功能 我们这里手动设置 其实是脱离了Archauis的管理 所以没法做到动态性 也没必要。
这就是为什么在获取属性的时候 最后用return properties.get(key)来兜底,因为 在开启动态配置的情况下 确实有可能出现 properties里面有 dynamicProperties里面没有的情况
总结
IClientConfig 就到这了 ,相对比较简单 了解主要的几个方法 以及大致的属性加载流程就好了