前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[享学Netflix] 八、Apache Commons Configuration2.x相较于1.x使用上带来哪些差异?

[享学Netflix] 八、Apache Commons Configuration2.x相较于1.x使用上带来哪些差异?

作者头像
YourBatman
发布2020-03-18 19:30:43
1.3K0
发布2020-03-18 19:30:43
举报

人不敬我,是我无才,我不敬人,是我无德,人不容我,是我无能,我不容人,是我无量,人不助我,是我无为,我不助人,是我无善。 代码下载地址:https://github.com/f641385712/netflix-learning

目录
  • 前言
  • 正文
    • 1.x和2.x的差异
      • 2.x新增类特别介绍
        • JSONConfiguration
        • YAMLConfiguration
        • ConfigurationPropertySource
      • GAV坐标
      • 运行时依赖
      • 基本使用
      • 事件-监听
      • 热加载
      • 文件扫描策略
      • 插值器
  • 总结
    • 声明

前言

Commons Configuration作为一个优秀的配置管理库,凭借着优秀的设计以及提供了热加载等使用功能,被不少其它组件作为基础配置管理组件使用,流行度较高。 从2004年发展至今,它一共有两个大版本:1.x和2.x。这两个大版本之前因为改过包名,并且GAV坐标也不一样,因此他俩:互不兼容,可以共存

虽然2.x早已成为主流,但前面解释过1.x版本也有学习的意义,所以避免不了共存的情况。因此本文将从多个方面,站在使用的角度比较两者的诸多不同,让同学们可以同时驾驭。

说明:此文仅讲使用,所以本人希望你是已经看过前面1-7篇:详细介绍1.x和2.x版本。这里可乘坐电梯直达:直达电梯


正文

下面将站在使用者的角度,从主流的使用方式上,比较两者差异。


1.x和2.x的差异

2.x新增类特别介绍

2.x版本新增了几个非常使用的类,在这里做简单介绍。

JSONConfiguration

此类是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

说明:占位符并未生效

YAMLConfiguration

此类是2.2版本后才有的,非常的新。使用前,需要先导入依赖包:

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.25</version>
</dependency>

步骤完全同上,只不过它解析的是Yaml格式文件,所以详细步骤略。 说明:它支持使用占位符

ConfigurationPropertySource

它只是作为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、忽略它


GAV坐标

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>

包名均发生了变化:

  • 1.x:org.apache.commons.configuration.Configuration
在这里插入图片描述
在这里插入图片描述
  • 2.x:org.apache.commons.configuration2.Configuration
在这里插入图片描述
在这里插入图片描述

因此他俩:互不兼容,可以共存


运行时依赖

1.x:

在这里插入图片描述
在这里插入图片描述

2.x:

在这里插入图片描述
在这里插入图片描述

两者均仅需核心依赖均可完成绝大部分功能。但2.x新增提供的JSONConfigurationYAMLConfiguration着实好用且紧跟主流,虽然它需要依赖额外的三方包~

说明: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是按照固定的顺序去查找、定位文件:

  1. user home
  2. 当前工厂classpath
  3. 系统classpath

2.x让这变得更加的灵活:允许应用程序自定义文件定位过程,这就是这个接口的作用。它有如下实现类:

在这里插入图片描述
在这里插入图片描述
  1. ClasspathLocationStrategy:从classpath下去加载文件(常用)
  2. AbsoluteNameLocationStrategy:绝对路径。所以直接使用new File(locator.getFileName())去加载
  3. HomeDirectoryLocationStrategy:从user.home里去查找
  4. BasePathLocationStrategy:使用basePath+fileName的方式new File()
  5. FileSystemLocationStrategy:使用文件系统定位。比如windows是带盘符的
  6. ProvidedURLLocationStrategy:直接是URL的方式。所以它也可以存在于网络上~
  7. CombinedLocationStrategy:真正使用的。它是一个聚合,这些实现类可以构成一个扫描链来进行按照其顺序进行组合扫描,之前讲过很多类似的设计模式了

这些顺序你可以自由组合,甚至还可以自定义。比如:

List<FileLocationStrategy> subs = Arrays.asList(
        new ProvidedURLLocationStrategy(),
        new FileSystemLocationStrategy(),
        new ClasspathLocationStrategy());
FileLocationStrategy strategy = new CombinedLocationStrategy(subs);

这样最终定位strategy 策略翻译如下:

  1. 使用自己的URL定位(比如有完整的URL路径)
  2. 文件系统
  3. 当前工程classpath(比如仅仅只有一个名字)

插值器

1.x核心API是:ConfigurationInterpolator 2.x核心API是:ConfigurationInterpolatorInterpolatorSpecificationLookup

例子暂略。


总结

关于2.x版本相较于1.x有哪些不一样就先介绍到这,本文是站在一个使用者的角度,对比两者在使用上的不一样,这对新手是特别具有引导意义的。 若想了解起深点的原理和定制,请参阅前面七篇文章,文末直接附有链接地址,直达阅读。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 前言
  • 正文
    • 1.x和2.x的差异
      • 2.x新增类特别介绍
      • GAV坐标
      • 运行时依赖
      • 基本使用
      • 事件-监听
      • 热加载
      • 文件扫描策略
      • 插值器
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档