Commons-Configuration2简介、使用方式、代码范例 -- 自动重新加载配置文件、监听器、处理器、自定义检测器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wanghantong/article/details/79072474

大纲:本专栏内容主要讲述Commons-Configuration2的常用和核心的使用方式,并不会逐一讲解其全部的功能。

github地址:

本章概述:

  1. 主要讲述了Commons-Configuration2的基本环境(jar包依赖)
  2. 讲述了在1.x版本和2.x版本下的初始化方式(基础版)
  3. 讲述了在2.X版本时,对该技术框架的可靠性和可用性处理
  4. 讲述了在1.x版本时和2.x版本时文件位置扫描策略的概述,重点讲述了2.x版本时,文件位置扫描的相关操作类并给出了一个案例和编码实现
  5. 讲述了自动重载资源文件的相关组件、流程、以及在使用过程中可能会犯的错误,并给出了一段基础的示例代码

pom.xml

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-configuration2 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-configuration2</artifactId>
    <version>2.2</version>
</dependency>

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

1. 初始化配置(version1.10)

# 使用有参构造来加载配置文件
Configuration config = new PropertiesConfiguration("usergui.properties");

# 使用无参构造初始化,然后通过load()方法来加载配置文件
Configuration config = new PropertiesConfiguration();

1. 初始化配置(Version2.1.1)(Recommend)

Configurations configs = new Configurations();
try
{
    Configuration config = configs.properties(new File("config.properties"));
    // access configuration properties

    String dbHost = config.getString("database.host");
    String dbPassword = config.getString("database.password", "secret");  // provide a default
    int dbPort = config.getInt("database.port");
    long dbTimeout = config.getLong("database.timeout");
    ...
}
catch (ConfigurationException cex)
{
    // Something went wrong
}

2. 注意:access configuration properties的异常

在获取properties文件的内容时:  

如果key不存在,且获取的类型为String类型时,那么返回值为null;
如果key不存在,且获取的类型为非String类型时,那么将抛出一个Exception: java.util.NoSuchElementException

注意:我们还可以指定一个默认的值,在找不到指定key的时候,Configuration将使用这个默认值, Configuration为每个取值的方法都提供了重载的方法。

3. 扫描规则:

Commons-Configuration 1.x
  • 如果指定的是绝对路径按绝对路径扫描

  • 如果指定的是相对路径,那么按如下顺序扫描

    1. in the current directory
    2. in the user home directory
    3. in the classpath
Commons-Configuration 2.x

在Version 2.0开始,对于文件扫描策略,用接口FileLocationStrategy来实现,该接口只有一个单一的方法locate(),

URL locate(FileSystem fileSystem, FileLocator locator);  

一共提供了如下几个实现类,分别来应对不同的场景。
ProvidedURLLocationStrategy, FileSystemLocationStrategy, AbsoluteNameLocationStrategy, BasePathLocationStrategy, HomeDirectoryLocationStrategy, ClasspathLocationStrategy, CombinedLocationStrategy,
同时, 这些实现类可以构成一个扫描链来进行按照其顺序进行组合扫描。

Starting with version 2.0, it is now possible to adapt this algorithm. The key to this is the `FileLocationStrategy` interface. The interface defines a single method:

举个例子:

As an example, consider that an application wants configuration files to be looked up (in this order)
假设我们期望按照如下的顺序进行扫描我们的配置文件:

1. by their URL
2. by the file system (which will evaluate base path and file name)
3. on the classpath  

代码实现如下:

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

4. 自动重载配置文件

我们都希望我们的系统是高可用的,在我们修改配置文件的以后,不期望去重新启动服务,而希望其能够自动的重新加载。

Commons-Configuration提供了一些组件来实现这一功能。

组件一:

ReloadingDetector Interface

组件二:

ReloadingController Class , ReloadingListener Interface

自动重载配置文件流程解析:

ReloadingController是一个完全功能(fully functional)的类,  
实现了执行重新加载检查的通用协议(基于外部触发器)并相应地作出反应。  
判断一个文件是否需要重新加载这个操作实际上是委托给了ReloadingDetector这个接口,  
当这个检测器(detector)发现了变化就将这一消息发送给已经注册好的监听器。 
ReloadingDetector本身并不会主动监听某一资源,它有一个 checkForReloading()方法来触发它重新加载资源,  
如果这个方法返回值为true,那么对于的controller将变成所谓的重新加载状态。  
这就表示着重载的请求被检测到了并且真正的开始重载。  

    Typically, this is done by one of the ReloadingListener objects registered at the controller.As long as the controller is in reloading state, no further changes on the configuration source monitored by the associated ReloadingDetector are detected.A manual invocation of the resetReloadingState() method is necessary to terminate this state and enable the detection of further changes.
注意:因为Commons-Configuration无法得知每个用户的期望触发重新加载的机制,所以其设计一个基于Timer的方案,作为一个最灵活的方式,同时它有提供了一个自定义触发器( a custom component triggering)组件的方法来适配各种不同的重载需求。

重中之重:

1. 重点一:

再次调用 builder's getConfiguration()方法时会创建和初始化一个新的Configuration实例(和我们要操作/修改的文件是绑定/关联的)。在这时候才会重载才会真正发生, controller的重载状态被重置。原文如下:

    The next call to the builder's getConfiguration() method causes a new configuration instance to be created and initialized from the content of the modified configuration file. At this time the reload actually happens, and the controller's reloading state is reset.  
2. 重点二:

我们使用builder来获取的Configuration实例是不能自动发生改变的,也就是说我用这个这个实例所获取的值,将贯穿应用的整个生命周期,当外部对文件进行修改的时候,通过该实例是无法获取到其变更的。正确的做法是保存builder这个引用,在每次获取配置文件的内容时使用一个新的Configuration实例,这样才能获取到变更后的值。原文如下:

    One important point to keep in mind when using this approach to reloading is that reloads are only functional if the builder is used as central component for accessing configuration data. The configuration instance obtained from the builder will not change automagically! So if an application fetches a configuration object from the builder at startup and then uses it throughout its life time, changes on the external configuration file become never visible. The correct approach is to keep a reference to the builder centrally and obtain the configuration from there every time configuration data is needed.
3. 关于PeriodicReloadingTrigger: (如果只创建了PeriodicReloadingTrigger实例但是没有start(), 那么此时也是无法检测到资源变化的)

该类包含有3个核心的方法,stop() and start()分别是暂停(pausing)和重新开始(resuming),当我们不在需要periodic trigger的时候,我们应该去调用shutdown()方法来释放所有的资源和终止定时检测器(terminates the scheduled executor service)。

代码部分 —— 基本例子:

    /**
     * @author Dragon
     * 
     *         <codeInfo> : 基础样例
     * 
     *         <Date> Jan 14, 2018
     * 
     * @throws Exception
     */
    public void basicDemo() throws Exception {

        Parameters params = new Parameters();
        // Read data from this file
        File propertiesFile = new File("commons.properties");

        // Use PropertiesConfiguration to read file
        ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder = new ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration>(
                PropertiesConfiguration.class).configure(params.fileBased().setFile(propertiesFile));

        // check the file per second
        PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(builder.getReloadingController(), null, 1,
                TimeUnit.SECONDS);

        // start trigger
        trigger.start();

        // check changes
        checkReloadResult(builder);

    }

举个例子:

    /**
     * @author Dragon
     * 
     *         <codeInfo> : 增加文件扫描策略
     * 
     *         <Date> Jan 14, 2018
     * 
     * @throws Exception
     */
    public void extend() throws Exception {

        //  @formatter:off
        Parameters params = new Parameters();

        // init file location strategy
        List<FileLocationStrategy> subs = Arrays.asList(
                new ProvidedURLLocationStrategy(),
                new FileSystemLocationStrategy(),
                new ClasspathLocationStrategy());// 此条被应用
        FileLocationStrategy strategy = new CombinedLocationStrategy(subs);

        // init BuilderParameters
        PropertiesBuilderParameters propertiesBuilderParameters = params.properties()
                .setEncoding("UTF-8")
                // Read data from this file
                // locate by FileSystemLocationStrategy
    //          .setFile(new File("/Users/Dragon/developCode/eclipse-workspace-javaee/java8/src/main/resources/commons.properties"))

                // locate by ClasspathLocationStrategy
                .setFile(new File("commons.properties"))
                .setLocationStrategy(strategy)
                .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
                .setReloadingRefreshDelay(2000L)
                .setThrowExceptionOnMissing(true);
        //  @formatter:on

        // Use PropertiesConfiguration to read file
        ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder = new ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration>(
                PropertiesConfiguration.class).configure(propertiesBuilderParameters);

        // check the file per second
        PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(builder.getReloadingController(), null, 1,
                TimeUnit.SECONDS);

        // start trigger
        trigger.start();

        checkReloadResult(builder);
    }  

本章概述:

本章主要对Commons-Configuration2自动重载进行了深入的研究学习,主要包含如下几个方面

  1. 自动加载的核心流程
  2. 讲述了ConfigurationBuilder的已有实现
  3. 讲述了使用动态修改配置的基本思路和方法
  4. 细化讲解了各个相关的接口和类
  5. 给出了代码范例和使用过程中易犯的错误
  6. 讲述了ReloadingController的使用

拓展研究

核心流程:

So the recipe to activate reloading for a builder instance is as follows:
  • Create and initialize the builder instance as usual.
  • Create a ReloadingDetector which is able to monitor the configuration source in question and to find out whether a reload action has to be performed. For this probably a custom implementation is required (as Commons Configuration currently supports only reloading detector implementations dealing with file handlers).
  • Create a ReloadingController object and initialize it with the ReloadingDetector created in the previous step.
  • Pass this reloading controllers to the builder’s connectToReloadingController() method.
  • Now reloading facilities are set up for this builder. In order to actually trigger reload checks ensure that the reloading controller’s checkForReloading() method is called at appropriate points of time (e.g. initiate a corresponding trigger as described earlier in this chapter.

代码步骤:

  1. 初始化参数BuilderParameters
  2. 构造ReloadingFileBasedConfigurationBuilder实例并初始化参数
  3. 配置检测器的策略并启动
  4. 动态获取Configuration实例并读取文件

1. 细说ConfigurationBuilder

在commons-Configuration2中,接口ConfigurationBuilder提供一个BasicConfigurationBuilder实现类,
同时又有三个子类继承了BasicConfigurationBuilder,它们分别是 CombinedConfigurationBuilder,
FileBasedConfigurationBuilder,
MultiFileConfigurationBuilder,
在这三个Builder之下,还有三个子类分别对应的是
ReloadingCombinedConfigurationBuilder, ReloadingFileBasedConfigurationBuilder<T extends FileBasedConfiguration>, ReloadingMultiFileConfigurationBuilder<T extends FileBasedConfiguration>
用来解决自动加载的问题。

使用ConfigurationBuilder来获取builder的实例
// Use PropertiesConfiguration to read file
ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder = new ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration>(
                PropertiesConfiguration.class).configure(params.fileBased().setFile(propertiesFile));
讲讲configure(BuilderParameters[] params)方法

BuilderParameters接口的层级结构:
MacDown Screenshot

使用builder获取对应的BasicBuilderProperties子类/接口:

Parameters params = new Parameters();  

CombinedBuilderParameters combined = params.combined();  

DatabaseBuilderParameters database = params.database();  

FileBasedBuilderParameters fileBased = params.fileBased();  

HierarchicalBuilderParameters hierarchical = params.hierarchical();  

MultiFileBuilderParameters multiFile = params.multiFile();  

PropertiesBuilderParameters properties = params.properties();  

JndiBuilderParameters jndi = params.jndi();  

INIBuilderParameters ini = params.ini();  

使用方法调用链为各个属性赋值:

FileBasedBuilderParameters fileBasedBuilderParameters = params.fileBased()
                .setFile(propertiesFile)
                .setEncoding("UTF-8")
                .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
                .setThrowExceptionOnMissing(true);

或者:

PropertiesBuilderParameters propertiesBuilderParameters = params.properties()
               .setFile(propertiesFile)
                .setEncoding("UTF-8")
                .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
                .setThrowExceptionOnMissing(true);

configure()方法中激活各个参数配置:

ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder = new ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration>(
                PropertiesConfiguration.class)
                        // 使用FileBasedBuilderParameters
                        .configure(fileBasedBuilderParameters);
                        // 使用PropertiesBuilderParameters
                        // .configure(propertiesBuilderParameters);

配置检查触发器并启动:

// check the file per second
PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(builder.getReloadingController(), null, 1,
                TimeUnit.SECONDS);  
// start trigger
trigger.start();  

代码范例及易错点:

    /**
     * @author Dragon
     * 
     *         <codeInfo> : 正确的初始化参数
     * 
     *         <Date> Jan 14, 2018
     * 
     * @throws Exception
     */
    public void parameterInit() throws Exception {
        //  @formatter:off
        Parameters params = new Parameters();

        // Read data from this file
        File propertiesFile = new File("commons.properties");

        // 参数在这里初始化是不起作用的,或者说暂时还不会用,要想此处配置生效,要使用这个流式配置的返回值然后传入到configure()方法中
        // params.fileBased()
        //      .setFile(propertiesFile)
        //      .setEncoding("UTF-8")
        //      .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
        //      .setThrowExceptionOnMissing(true);

        // 详细参考BuilderParameters的子接口和实现类
        // 用其返回值作为configure()方法的参数[使用FileBasedBuilderParameters]
        FileBasedBuilderParameters fileBasedBuilderParameters = params.fileBased()
                .setFile(propertiesFile)
                .setEncoding("UTF-8")
                .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
                .setThrowExceptionOnMissing(true);

        // 用其返回值作为configure()方法的参数[使用PropertiesBuilderParameters]
//      PropertiesBuilderParameters propertiesBuilderParameters = params.properties()
//              .setFile(propertiesFile)
//              .setEncoding("UTF-8")
//              .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
//              .setThrowExceptionOnMissing(true);

        // Use PropertiesConfiguration to read file
        // 要想params配置的属性生效一定是要在configure()中配置的才可以原因是configure()方法要的参数是BuilderParameters[],但是原生的Parameters并非为该类型或者其子类,所以之前配置的params的相关设置不生效
        ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder = new ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration>(
                PropertiesConfiguration.class)
                        // 使用FileBasedBuilderParameters
                        .configure(fileBasedBuilderParameters);
                        // 使用PropertiesBuilderParameters
                        // .configure(propertiesBuilderParameters);
        //  @formatter:on

        // check the file per second
        PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(builder.getReloadingController(), null, 1,
                TimeUnit.SECONDS);

        // start trigger
        trigger.start();

        // get original value from file
         checkReloadResult(builder);

    }  

本章概述:

  1. Commons-Configuration2监听器的作用
  2. 监听器的层级结构
  3. 常用的监听器
  4. 场景模拟与使用范例

2. 增加监听

监听的分类:
基于Configuration的Listener: ConfigurationEvent

基于ConfigurationBuilder的Listener: ConfigurationBuilderEvent

Event.ANY

ConfigurationBuilderEvent.ANY A common super type for all events produced by a configuration builder. An event listener registered for this event type receives all notifications about a configuration builder.  

ConfigurationBuilderEvent.RESET The managed configuration of a builder has been reset. This means that the configuration is now obsolete. A new object is created the next time the builder's getConfiguration() method is called.  

ConfigurationBuilderEvent.CONFIGURATION_REQUEST This event is generated when the builder's getConfiguration() method is entered, but before the managed configuration is actually accessed. This is an opportunity to perform some manipulations which might also affect the managed configuration. One use case is to trigger a reloading check at this point of time. If it turns out that a reload is required, the managed configuration gets invalidated and is replaced by a new object - which is then directly returned by the current method call.  

ConfigurationBuilderResultCreatedEvent.RESULT_CREATED A new managed configuration object has been created. This event is fired initially on first invocation of the getConfiguration() method, and then again after the managed configuration has been reset and created anew. A reference to the new configuration object can be obtained from the event so that specific initializations can be performed.  
代码范例: 使用监听结合日志的方式记录配置文件被修改记录

持续更新中…

猜你喜欢

转载自blog.csdn.net/wanghantong/article/details/79072474