要相信:你遇到的问题,肯定不止你一个人遇到过。
–> 返回专栏总目录 <–
代码下载地址:https://github.com/f641385712/netflix-learning
目录
前言
上面文章介绍了Netflix Archaius
对Commons Configuration
核心API Configuration
的扩展实现,知道了Netflix Archaius
依赖于Commons Configuration
并且在其基础上做了扩展和增强。
本文将继续夯实基础,聊聊它的另外两个核心API:配置管理器ConfigurationManager
和动态属性支持DynamicPropertySupport
。
正文
ConfigurationManager
:配置管理器。目的是屏蔽使用者对Configuration
这个API的感知,让其只懂如何调用即可。
DynamicPropertySupport
:对属性的动态化提供支持的接口。同时也顺便用于解耦动态属性对Commons Configuration
的依赖(虽然目前唯一实现只有它)
ConfigurationManager
配置管理器,管理系统范围配置和部署上下文DeploymentContext的中心位置。
它是个工具类,所有方法均以static静态方法提供:
public class ConfigurationManager {
// 注意:volatile关键字保证了内存可见性
static volatile AbstractConfiguration instance = null;
// 标志:是否自定义的Configuration安装进来了
// true:使用的你自定义的Configuration实例
// false:使用系统默认的实例(默认)
static volatile boolean customConfigurationInstalled = false;
private static volatile ConfigMBean configMBean = null;
// 部署上下文接口
static volatile DeploymentContext context = null;
// initStack将在配置管理器静态初始化时保存堆栈跟踪
// 帮助调试何时何地创建了ConfigurationManager。我们这样做是为了帮助调试问题
private static StackTraceElement[] initStack = null;
... // 省略一些常量
// 静态代码块:完成一些静态初始化动作
static {
initStack = Thread.currentThread().getStackTrace();
// 1、根据系统属性,创建一个AbstractConfiguration
String className = System.getProperty("archaius.default.configuration.class");
String factoryName = System.getProperty("archaius.default.configuration.factory");
instance = (AbstractConfiguration) Class.forName(className).newInstance();
customConfigurationInstalled = true;
...
... // 同样的逻辑给`DeploymentContext context`赋值
}
public static void setDeploymentContext(DeploymentContext context) { ... }
public static StackTraceElement[] getStaticInitializationSource() {
return initStack;
}
// AbstractConfiguration instance是否已经初始化了
// 只要有这些系统属性就能初始化,否则是不行的了
public static synchronized boolean isConfigurationInstalled() {
return customConfigurationInstalled;
}
}
静态代码块的初始化过程,可总结为如下步骤:
- 记录当前堆栈,方便查问题(毕竟静态代码块你可能不知道是啥时候执行的)
- 给静态成员变量
AbstractConfiguration instance
赋值:- 若有系统属性
archaius.default.configuration.class
,那就使用Class.newInstance()创建实例(使用自定义的) - 若有系统属性
archaius.default.configuration.factory
,那就反射调用其getInstance()
方法得到实例(使用自定义的) - 若二者均没有,那就交给系统帮你构建
- 若有系统属性
- 给静态成员变量
DeploymentContext context
赋值,逻辑完全同上,两个key是:archaius.default.deploymentContext.class
archaius.default.deploymentContext.factory
- 它顺带会把
DeploymentContext.ContextKey.values()
所有key放进上的instance里面
作为一个管理器,自然有添加、获取等方法,看看这些主要的API:
ConfigurationManager:
// 如果静态代码块里没有初始化,这里就交给系统默认帮你完成初始化
// 该方法是Synchronized同步方法,双重校验锁
public static AbstractConfiguration getConfigInstance() {
if (instance == null) {
synchronized (ConfigurationManager.class) {
if (instance == null) {
// getBoolean()没有此key默认返回false
// 系统属性archaius.dynamicProperty.disableDefaultConfig= true可以关闭默认行为
instance = getConfigInstance(Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_CONFIG));
}
}
}
return instance;
}
// 默认行为做了两件事:创建instance实例 以及注册MBean支持JMX
// 当然:开启JMX支持与否由archaius.dynamicPropertyFactory.registerConfigWithJMX属性指定,默认是false
private static AbstractConfiguration getConfigInstance(boolean defaultConfigDisabled) {
if (instance == null && !defaultConfigDisabled) {
instance = createDefaultConfigInstance();
registerConfigBean();
}
return instance;
}
// 创建默认的Configuration,它是一个ConcurrentCompositeConfiguration组合的配置
private static AbstractConfiguration createDefaultConfigInstance() {
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
...
new DynamicURLConfiguration()
new SystemConfiguration()
new EnvironmentConfiguration()
...
return config;
}
该管理器主要管理着一个AbstractConfiguration
,而这个实例的创建方式你可以选择:
- 自己指定:设置如上两个系统属性均可
- 自动创建:若没有手动指定,那就走自动创建逻辑。
默认创建的AbstractConfiguration
实例是一个组合配置ConcurrentCompositeConfiguration
,它包含有如下三种Configuration
:
- SystemConfiguration
- EnvironmentConfiguration
DynamicURLConfiguration
前两个好说,最主要是DynamicURLConfiguration
这个喽,它会自动去加载类路径下名为config.properties
的资源以及你自己指定的archaius.configurationSource.additionalUrls
下的资源等,并且开启对它们的自动轮询。
说明:关于
DynamicURLConfiguration
的使用是上篇文章的详细内容,本文不做详细描述,请移步查看
其它的相关方法如:getPropertiesFromFile/loadProperties/loadPropertiesFromConfiguration
等等比较简单,就不用过多介绍了。
使用示例
@Test
public void fun1() {
// 为方便打印,禁用调用系统属性们
System.setProperty(ConfigurationManager.DISABLE_DEFAULT_SYS_CONFIG, "true");
System.setProperty(ConfigurationManager.DISABLE_DEFAULT_ENV_CONFIG, "true");
// config.removeConfiguration(ConfigurationManager.SYS_CONFIG_NAME);
// config.removeConfiguration(ConfigurationManager.ENV_CONFIG_NAME);
// 自定义Configuration实现
// System.setProperty("archaius.default.configuration.class", "com.xxxx.XXXConfiuration");
ConcurrentCompositeConfiguration config = (ConcurrentCompositeConfiguration) ConfigurationManager.getConfigInstance();
ConfigurationUtils.dump(config, System.out);
System.out.println("\n=================================");
Properties properties = new Properties();
properties.put("age", 18);
// ConfigurationManager.loadPropertiesFromConfiguration();
ConfigurationManager.loadProperties(properties);
ConfigurationUtils.dump(config, System.out);
System.out.println("\n=================================");
}
运行程序,打印:
name=YourBatman
=================================
name=YourBatman
age=18
=================================
config.properties
里的属性是支持动态修改的,是DynamicURLConfiguration
提供的支持,这里就不做过多演示了,详情见前一篇文章。
另外,可以看到ConfigurationManager.loadProperties(properties);
都是新增,而非覆盖的形式。
说明:底层使用的是
config.setProperty()
,所以如果是相同key,那就后者覆盖前者喽
DynamicPropertySupport
动态属性支持接口。对于该接口的实现,官方说了:大多数情况下辅以Apache Commons Configuration
来实现会容易很多,但这个接口可以让你在不依赖于Apache Commons Configuration
库仍可以正常使用。
说明:说是这么说,但是依赖于
Apache Commons Configuration
实现更方面,为什么不呢?
public interface DynamicPropertySupport {
// 获取指定属性的值,的字符串表现形式
// 该值会被缓存,然后被转换为DynamicProperty的指定类型
String getString(String propName);
// 添加属性更改监听器。这对于DynamicProperty是必需的
// 在底层DynamicPropertySupport中更新属性后接收回调
void addConfigurationListener(PropertyListener expandedPropertyListener);
}
它仅有一个内置实现:ConfigurationBackedDynamicPropertySupportImpl
。
ConfigurationBackedDynamicPropertySupportImpl
从命名ConfigurationBacked
中可以看出它表达的意思:基于org.apache.commons.configuration.Configuration
支持的实现。
public class ConfigurationBackedDynamicPropertySupportImpl implements DynamicPropertySupport {
private final AbstractConfiguration config;
... // 省略构造器,必须给config赋值
// 如果是String直接返回
// 若是String数组:转换为逗号分隔的字符串
// 若是其它类型(如Object类型),直接toString()
@Override
public String getString(String key) {
try {
String values[] = config.getStringArray(key);
if (values == null) {
return null;
}
if (values.length == 0) {
return config.getString(key);
} else if (values.length == 1) {
return values[0];
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
sb.append(values[i]);
if (i != values.length - 1) {
sb.append(",");
}
}
return sb.toString();
} catch (Exception e) {
Object v = config.getProperty(key);
if (v != null) {
return String.valueOf(v);
} else {
return null;
}
}
}
// 它是实现了org.apache.commons.configuration.event.ConfigurationListener的几个监听器实现类
// 目的当然是适配`PropertyListener`监听器喽,当属性改变时触发对应事件
@Override
public void addConfigurationListener(PropertyListener expandedConfigListener) {
ExpandedConfigurationListenerAdapter nl = new ExpandedConfigurationListenerAdapter(expandedConfigListener);
config.addConfigurationListener(nl);
}
}
虽然说动态属性DynamicProperty
的API设计可以通过DynamicPropertySupport``Apache Commons Configuration
,但是实际使用中使用的肯定是ConfigurationBackedDynamicPropertySupportImpl
,因为我们不可能自己去书写一套配置管理。
使用示例
@Test
public void fun2() throws ConfigurationException {
PropertiesConfiguration config = new PropertiesConfiguration("config.properties");
ConfigurationBackedDynamicPropertySupportImpl dynamicPropertySupport = new ConfigurationBackedDynamicPropertySupportImpl(config);
// 添加一个PropertyListener监听器,监听属性的增加、set(修改)、清空
dynamicPropertySupport.addConfigurationListener(new AbstractDynamicPropertyListener() {
@Override
public void handlePropertyEvent(String name, Object value, EventType eventType) {
System.out.printf("事件类型:%s key是 %s 修改后的值是 %s\n", eventType, name, value);
System.out.println("-------------------------------------");
}
// 请注意:这个事件对应AbstractConfiguration.EVENT_CLEAR,若你自己不实现,父类是空实现的哦
@Override
public void clear(Object source, boolean beforeUpdate) {
//EventType里并没有对应它的实现,所以这样是行不通的
// if (!beforeUpdate) {
// handlePropertyEvent(null, null, EventType.CLEAR);
// }
if (source instanceof Configuration && beforeUpdate) { // 必须是before,因为after就没值啦
System.out.println("清空所有的事件,清空的值情况如下:");
ConfigurationUtils.dump((Configuration) source, System.out);
System.out.println("\n-------------------------------------");
}
}
});
config.addProperty("age", 18);
config.setProperty("age", 20);
config.clearProperty("name"); // 事件:AbstractConfiguration.EVENT_CLEAR_PROPERTY
config.clear(); // 事件:AbstractConfiguration.EVENT_CLEAR
}
控制台打印:
事件类型:ADD key是 age 修改后的值是 18
-------------------------------------
事件类型:SET key是 age 修改后的值是 20
-------------------------------------
事件类型:CLEAR key是 name 修改后的值是 null
-------------------------------------
清空所有的事件,清空的值情况如下:
age=20
-------------------------------------
DynamicPropertySupport
的主要作用便是这样:当你属性发生变更时,能对应的发出修改事件从而方便做出对应响应。DynamicProperty
便是通过其内置的DynamicPropertyListener
监听器来实现属性动态化的,下文详解。
总结
关于Netflix Archaiu
的两大核心API:ConfigurationManager
和DynamicPropertySupport
就介绍到这了,有了前面的基础普遍,本文理解起来相对容易。
至此我们已经了解到了Netflix Archaiu
动态属性的核心支持,为接下来掌握其动态属性的使用、原理打下了坚持基础,下文将会介绍DynamicProperty
。
声明
原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭
。你也可【左边扫码/或加wx:fsx641385712】邀请你加入我的 Java高工、架构师 系列群大家庭学习和交流。
- [享学Netflix] 一、Apache Commons Configuration:你身边的配置管理专家
- [享学Netflix] 二、Apache Commons Configuration事件监听机制及使用ReloadingStrategy实现热更新
- [享学Netflix] 三、Apache Commons Configuration2.x全新的事件-监听机制
- [享学Netflix] 四、Apache Commons Configuration2.x文件定位系统FileLocator和FileHandler
- [享学Netflix] 五、Apache Commons Configuration2.x别样的Builder模式:ConfigurationBuilder
- [享学Netflix] 六、Apache Commons Configuration2.x快速构建工具Parameters和Configurations
- [享学Netflix] 七、Apache Commons Configuration2.x如何实现文件热加载/热更新?
- [享学Netflix] 八、Apache Commons Configuration2.x相较于1.x使用上带来哪些差异?
- [享学Netflix] 九、Netflix Archaius配置管理库:初体验及基础API详解
- [享学Netflix] 十、Netflix Archaius对Commons Configuration核心API Configuration的扩展实现
来源:CSDN
作者:YourBatman
链接:https://blog.csdn.net/f641385712/article/details/104444083