人不敬我,是我无才,我不敬人,是我无德,人不容我,是我无能,我不容人,是我无量,人不助我,是我无为,我不助人,是我无善。 代码下载地址:https://github.com/f641385712/netflix-learning
Commons Configuration
作为一个优秀的配置管理库,凭借着优秀的设计以及提供了热加载等使用功能,被不少其它组件作为基础配置管理组件使用,流行度较高。
从2004年发展至今,它一共有两个大版本:1.x和2.x。这两个大版本之前因为改过包名,并且GAV坐标也不一样,因此他俩:互不兼容,可以共存。
虽然2.x早已成为主流,但前面解释过1.x版本也有学习的意义,所以避免不了共存的情况。因此本文将从多个方面,站在使用的角度比较两者的诸多不同,让同学们可以同时驾驭。
说明:此文仅讲使用,所以本人希望你是已经看过前面1-7篇:详细介绍1.x和2.x版本。这里可乘坐电梯直达:直达电梯
下面将站在使用者的角度,从主流的使用方式上,比较两者差异。
2.x版本新增了几个非常使用的类,在这里做简单介绍。
此类是2.2版本后才有的,非常的新。一个能够解析JSON的专门的层次结构配置类 文档。使用前,需要先导入依赖包:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.1</version>
</dependency>
类路径下放置一个JSON文件:
{
"name": "YourBatman",
"age": 18,
"son": {
"name": "${name}-son",
"age": 2
}
}
使用读取:
@Test
public void fun1() throws ConfigurationException {
// InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("1.json");
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("1.json");
JSONConfiguration configuration = new JSONConfiguration();
configuration.read(inputStream);
ConfigurationUtils.dump(configuration,System.out);
}
运行程序打印:
name=YourBatman
age=18
son.name=${name}-son
son.age=2
说明:占位符并未生效
此类是2.2版本后才有的,非常的新。使用前,需要先导入依赖包:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.25</version>
</dependency>
步骤完全同上,只不过它解析的是Yaml格式文件,所以详细步骤略。 说明:它支持使用占位符
它只是作为org.springframework.core.env.PropertySource
的一个实现,继承自EnumerablePropertySource
,并且是用org.apache.commons.configuration2.Configuration
来管理配置喽。
public class ConfigurationPropertySource extends EnumerablePropertySource<Configuration> {
... // 省略构造器
@Override
public Object getProperty(final String name) {
return source.getProperty(name);
}
...
}
源码非常简单:使用Configuration
装载Spring的配置而已,因此它也可以同Map一样,作为一个Spring的配置源PropertySource
来来使用哦,例子略。
说明:如果不太熟悉Spring的Enviroment抽象以及属性源
PropertySource
的同学理解起来相对费解些,我给出两个建议:1、去了解它 2、忽略它
1.x:
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
2.x:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.6</version>
</dependency>
包名均发生了变化:
org.apache.commons.configuration.Configuration
org.apache.commons.configuration2.Configuration
因此他俩:互不兼容,可以共存。
1.x:
2.x:
两者均仅需核心依赖均可完成绝大部分功能。但2.x新增提供的JSONConfiguration
和YAMLConfiguration
着实好用且紧跟主流,虽然它需要依赖额外的三方包~
说明:Spring Boot对Yaml的解析依赖的也是
org.yaml:snakeyaml
哦,所以契合得特别好 另外,Configuration Builder几乎一定会用,所以2.x请也导入commons-beanutils
这个依赖(1.x都是自己new的,所以不用导入)
1.x:
@Test
public void fun1() throws ConfigurationException {
Configuration config = new PropertiesConfiguration("1.properties");
System.out.println(config.getString("common.name"));
}
2.x:
@Test
public void fun1() throws ConfigurationException {
Configurations configs = new Configurations();
// 没有带参的构造器了,只能通过Configurations构建出来
// Configuration configuration = new PropertiesConfiguration("1.properties");
PropertiesConfiguration config = configs.properties("1.properties");
// configs.xml();
new SystemConfiguration();
new EnvironmentConfiguration();
System.out.println(config.getString("common.name"));
}
说明:2.x使用
Configurations
必须额外导入commons-beanutils
这个jar。
另外,2.x大多时候构建Configuration
使用的应该是Builder模式,具体详情请参考前面5篇文章。
2.x
完全摒弃了1.x版本的设计,完全重新设计了一套。使用方式上自然也稍有变化:
1.x:
@Test
public void fun4() throws ConfigurationException {
PropertiesConfiguration configuration = new PropertiesConfiguration("1.properties");
// 注册一个监听器
configuration.addConfigurationListener(event -> {
Object source = event.getSource(); // 事件源
int type = event.getType();
if (!event.isBeforeUpdate()) { // 只关心update后的事件,否则会执行两次哦,请务必注意
System.out.println("事件源:" + source.getClass());
System.out.println("事件type类型:" + type);
// 处理你自己的逻辑
}
});
// 增加一个属性,会同步触发监听器去执行
configuration.addProperty("common.addition", "additionOne");
System.out.println(configuration.getString("common.addition"));
}
2.x:
@Test
public void fun2() throws ConfigurationException {
Configurations configs = new Configurations();
PropertiesConfiguration config = configs.properties("1.properties");
// ConfigurationEvent内置有很多事件类型可使用
// 若不满足条件,请你自定义事件类型
config.addEventListener(ConfigurationEvent.ADD_PROPERTY, event -> {
Object source = event.getSource(); // 事件源
EventType<? extends Event> eventType = event.getEventType();
if (!event.isBeforeUpdate()) {
System.out.println("事件源:" + source.getClass());
System.out.println("事件type类型:" + eventType);
}
});
// 添加属性 触发事件
config.addProperty("common.addition", "additionOne");
System.out.println(config.getString("common.addition"));
}
执行结果,打印:
事件源:class org.apache.commons.configuration2.PropertiesConfiguration
事件type类型:EventType [ ADD_PROPERTY ]
additionOne
1.x:
@Test
public void fun5() throws ConfigurationException {
PropertiesConfiguration configuration = new PropertiesConfiguration("1.properties");
// 监听到配置文件被重新加载了就输出一条日志喽~
configuration.addConfigurationListener(event -> {
// 只监听到重新加载事件
if (event.getType() == PropertiesConfiguration.EVENT_RELOAD) {
System.out.println("配置文件重载...");
configuration.getKeys().forEachRemaining(k -> {
System.out.println("/t " + k + "-->" + configuration.getString(k));
});
}
});
// 使用文件改变重载策略:让改变文件能热加载
FileChangedReloadingStrategy reloadingStrategy = new FileChangedReloadingStrategy();
reloadingStrategy.setRefreshDelay(3000L); // 设置最小事件间隔,单位是毫秒
configuration.setReloadingStrategy(reloadingStrategy);
// 使用另外一个线程模拟去get
otherThreadGet(configuration);
// hold住main线程,不让程序终止
while (true) {}
}
private void otherThreadGet(PropertiesConfiguration configuration) {
new Thread(() -> {
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
configuration.getString("commmon.name");
}
}).start();
}
2.x:
@Test
public void fun22() {
// 关联上1.properties这个文件
Map<String, Object> map = new HashMap<>();
map.put("fileName", "1.properties");
// 因fileHandler此例不需要FileBased,所以先用null吧
FileHandler fileHandler = new FileHandler(null, FileHandler.fromMap(map));
// 使用控制器ReloadingController 代理掉ReloadingDetector来使用,更好用
ReloadingController reloadingController = new ReloadingController(new FileHandlerReloadingDetector(fileHandler));
reloadingController.addEventListener(ReloadingEvent.ANY, event -> {
ReloadingController currController = event.getController();
Object data = event.getData();
currController.resetReloadingState(); // 需要手动充值一下,否则下次文件改变就不会发送此事件啦
System.out.println((reloadingController == currController) + " data:" + data);
});
// 准备定时器:用于监控文件的的变化:3秒看一次 注意一定要start()才能生效哦
new PeriodicReloadingTrigger(reloadingController, "自定义数据", 3, TimeUnit.SECONDS).start();
// hold住主线程
while (true) { }
}
1.x是按照固定的顺序去查找、定位文件:
2.x让这变得更加的灵活:允许应用程序自定义文件定位过程,这就是这个接口的作用。它有如下实现类:
new File(locator.getFileName())
去加载user.home
里去查找CombinedLocationStrategy
:真正使用的。它是一个聚合,这些实现类可以构成一个扫描链来进行按照其顺序进行组合扫描,之前讲过很多类似的设计模式了这些顺序你可以自由组合,甚至还可以自定义。比如:
List<FileLocationStrategy> subs = Arrays.asList(
new ProvidedURLLocationStrategy(),
new FileSystemLocationStrategy(),
new ClasspathLocationStrategy());
FileLocationStrategy strategy = new CombinedLocationStrategy(subs);
这样最终定位strategy 策略翻译如下:
1.x核心API是:ConfigurationInterpolator
2.x核心API是:ConfigurationInterpolator
和InterpolatorSpecification
、Lookup
…
例子暂略。
关于2.x版本相较于1.x有哪些不一样就先介绍到这,本文是站在一个使用者的角度,对比两者在使用上的不一样,这对新手是特别具有引导意义的。 若想了解起深点的原理和定制,请参阅前面七篇文章,文末直接附有链接地址,直达阅读。