1. 程式人生 > >Commons-Configuration2簡介、使用方式、程式碼範例 -- 自動重新載入配置檔案、監聽器、處理器、自定義檢測器

Commons-Configuration2簡介、使用方式、程式碼範例 -- 自動重新載入配置檔案、監聽器、處理器、自定義檢測器

大綱:本專欄內容主要講述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.  
程式碼範例: 使用監聽結合日誌的方式記錄配置檔案被修改記錄

持續更新中…