欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實現(xiàn)配置加載機制

 更新時間:2016年01月30日 16:30:29   投稿:hebedich  
這篇文章主要介紹了Java實現(xiàn)配置加載機制的相關(guān)資料,需要的朋友可以參考下

前言

現(xiàn)如今幾乎大多數(shù)Java應用,例如我們耳熟能詳?shù)膖omcat, struts2, netty...等等數(shù)都數(shù)不過來的軟件,
要滿足通用性,都會提供配置文件供使用者定制功能。

甚至有一些例如Netty這樣的網(wǎng)絡框架,幾乎完全就是由配置驅(qū)動,這樣的軟件我們也通常稱之為"微內(nèi)核架構(gòu)"的軟件。
你把它配置成什么,它就是什么。

It is what you configure it to be.
最常見的配置文件格式是XML, Properties等等文件。

本文探討加載配置中最通用也是最常見的場景,那就是把一個配置文件映射成Java里的POJO對象.
并探討如何實現(xiàn)不同方式的加載,例如,有一些配置是從本地XML文件里面加載的,而有一些配置需要從本地Properties文件加載,更有甚者,有一些配置需要通過網(wǎng)絡加載配置。

如何實現(xiàn)這樣一個配置加載機制,讓我們擁有這個機制后,不會讓加載配置的代碼散布得到處都是,并且可擴展,可管理。

配置加載器

首先,我們需要一個配置加載器,而這個配置加載器是可以有多種不同的加載方式的,因此,我們用一個接口來描述它,如下所示:

/**
 * 
 *
 * @author Bean
 * @date 2016年1月21日 上午11:47:12
 * @version 1.0
 *
 */
public interface IConfigLoader<T> {
  
  /**
   * load the config typed by T
   *
   * @return
   * @throws ConfigException
   */
  public T load() throws ConfigException;
}

可是,為什么我們需要在這個接口上聲明泛型<T> ?
很明顯,當我們要使用一個配置加載器時,你得告訴這個配置加載器你需要加載后得到什么結(jié)果。
例如,你希望加載配置后得到一個AppleConfig對象,那么你就可以這么去使用上述定義的接口:

  IConfigLoader<AppleConfig> loader = new AppleConfigLoader<AppleConfig>();
  AppleConfig config = loader.load();

于是你將配置文件里的信息轉(zhuǎn)化成了一個AppleConfig對象,并且你能得到這個AppleConfig對象實例。

到目前,貌似只要我們的AppleConfigLoader里面實現(xiàn)了怎么加載配置文件的具體勞動,我們就可以輕易加載配置了。

可以這么說,但是不是還沒有考慮到,配置可能通過不同的方式加載呢,比如通過Properties加載,通過dom方式加載,通過sax方式加載,或者通過某些第三方的開源庫來加載。

因此,除了配置加載器,我們還需要另外一種角色,配置加載方式的提供者。暫且,我們就叫它IConfigProvider。

配置加載方式的提供者

配置加載方式的提供者可以提供一種加載方式給配置加載器,換言之,提供一個對象給配置加載器。

如果通過dom方式加載,那么提供者提供一個Document對象給加載器。
如果通過Properties方式加載,那么提供者提供一個Properties對象給加載器
如果通過第三方類庫提供的方式加載,比如apache-commons-digester3(tomcat的配置加載),那么提供者提供一個Digester對象給加載器
提供者的職責就是提供,僅此而已,只提供配置加載器所需要的對象,但它本身并不參與配置加載的勞動。

我們用一個接口IConfigProvider來定義這個提供者

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 上午11:54:28
 * @version 1.0
 *
 */
public interface IConfigProvider<T> {

  /**
   * provide a config source used for loading config
   *
   * @return
   * @throws ConfigException
   */
  public T provide() throws ConfigException;
}

這里為什么又會有<T>來聲明泛型呢?
如果需要一個提供者,那么至少得告訴這個提供者它該提供什么吧。

因此,一個提供者會提供什么,由這個來決定。

同時,到這里,我們可以先建造一個工廠,讓它來生產(chǎn)特定的提供者:

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 上午11:56:28
 * @version 1.0
 *
 */
public class ConfigProviderFactory {

  private ConfigProviderFactory() {
    throw new UnsupportedOperationException("Unable to initialize a factory class : "
        + getClass().getSimpleName());
  }

  public static IConfigProvider<Document> createDocumentProvider(String filePath) {
    return new DocumentProvider(filePath);
  }

  public static IConfigProvider<Properties> createPropertiesProvider(String filePath) {
    return new PropertiesProvider(filePath);
  }
  
  public static IConfigProvider<Digester> createDigesterProvider(String filePath) {
      return new DigesterProvider(filePath);
  }
}

可以開始實現(xiàn)具體配置加載器了?

還不行!

到這里,假設我們有一個配置文件,叫apple.xml。而且我們要通過DOM方式把這一份apple.xml加載后變成AppleConfig對象。

那么,首先我要通過提供者工廠給我制造一個能提供Document的提供者。然后拿到這個提供者,我就可以調(diào)用它的provide方法來獲得Document對象,
有了document對象,那么我就可以開始來加載配置了。

可是,如果要加載BananaConfig、PearConfig.......呢,其步驟都是一樣的。因此我們還要有一個抽象類,來實現(xiàn)一些默認的共同行為。

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 上午11:59:19
 * @version 1.0
 *
 */
public abstract class AbstractConfigLoader <T, U> implements IConfigLoader<T>{

  protected IConfigProvider<U> provider;
  
  protected AbstractConfigLoader(IConfigProvider<U> provider) {
    this.provider = provider;
  }

  /*
   * @see IConfigLoader#load()
   */
  @Override
  public T load() throws ConfigException {
    return load(getProvider().provide());
  }

  public abstract T load(U loaderSource) throws ConfigException;
  
  protected IConfigProvider<U> getProvider() {
    return this.provider;
  }
}

每個配置加載器都有一個帶參數(shù)構(gòu)造器,接收一個Provider。

泛型指明了我要加載的是AppleConfig還是BananConfig,泛型<U>指明了要用什么加載方式加載,是Document呢,還是Properties,或者其他。

實戰(zhàn)運用實例

有一份菜市場配置文件market.xml,配置了菜市場的商品,里面有兩種商品,分別是蘋果和雞蛋。

<market>
  <apple>
    <color>red</color>
    <price>100</price>
  </apple>
  <egg>
    <weight>200</weight>
  </egg>
</market>

另外還有一份關(guān)于各個檔口老板名字的配置文件,owner.properties

port1=Steve Jobs
port2=Bill Gates
port3=Kobe Bryant

我們先定義好如下類:
MarketConfig.java

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 下午11:03:37
 * @version 1.0
 *
 */
public class MarketConfig {

  private AppleConfig appleConfig;
  private EggConfig eggConfig;
  private OwnerConfig ownerConfig;
  
  public AppleConfig getAppleConfig() {
    return appleConfig;
  }
  public void setAppleConfig(AppleConfig appleConfig) {
    this.appleConfig = appleConfig;
  }
  public EggConfig getEggConfig() {
    return eggConfig;
  }
  public void setEggConfig(EggConfig eggConfig) {
    this.eggConfig = eggConfig;
  }
  public OwnerConfig getOwnerConfig() {
    return ownerConfig;
  }
  public void setOwnerConfig(OwnerConfig ownerConfig) {
    this.ownerConfig = ownerConfig;
  }
}

AppleConfig.java

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 下午11:03:45
 * @version 1.0
 *
 */
public class AppleConfig {

  private int price;
  private String color;
  
  public void setPrice(int price) {
    this.price = price;
  }
  
  public int getPrice() {
    return this.price;
  }
  
  public void setColor(String color) {
    this.color = color;
  }
  
  public String getColor() {
    return this.color;
  }
}

EggConfig.java

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 下午11:03:58
 * @version 1.0
 *
 */
public class EggConfig {

  private int weight;
  
  public void setWeight(int weight) {
    this.weight = weight;
  }
  
  public int getWeight() {
    return this.weight;
  }
}

OwnerConfig.java

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 下午11:04:06
 * @version 1.0
 *
 */
public class OwnerConfig {

  private Map<String, String> owner = new HashMap<String, String>();
  
  public void addOwner(String portName, String owner) {
    this.owner.put(portName, owner);
  }
  
  public String getOwnerByPortName(String portName) {
    return this.owner.get(portName);
  }
  
  public Map<String, String> getOwners() {
    return Collections.unmodifiableMap(this.owner);
  }
}

這個例子有兩種配置加載方式,分別是Dom和Properties加載方式。
所以我們的提供者建造工廠需要制造兩種提供者provider.
而且需要定義2個配置加載器,分別是:

OwnerConfigLoader

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 下午11:24:50
 * @version 1.0
 *
 */
public class OwnerConfigLoader extends AbstractConfigLoader<OwnerConfig, Properties>{

  /**
   * @param provider
   */
  protected OwnerConfigLoader(IConfigProvider<Properties> provider) {
    super(provider);
  }

  /* 
   * @see AbstractConfigLoader#load(java.lang.Object)
   */
  @Override
  public OwnerConfig load(Properties props) throws ConfigException {
    OwnerConfig ownerConfig = new OwnerConfig();
    
    /**
     * 利用props,設置ownerConfig的屬性值
     * 
     * 此處代碼省略
     */
    return ownerConfig;
  }
}

然后是MarketConfigLoader

import org.w3c.dom.Document;

/**
 *
 *
 * @author Bean
 * @date 2016年1月21日 下午11:18:56
 * @version 1.0
 *
 */
public class MarketConfigLoader extends AbstractConfigLoader<MarketConfig, Document> {

  /**
   * @param provider
   */
  protected MarketConfigLoader(IConfigProvider<Document> provider) {
    super(provider);
  }

  /* 
   * AbstractConfigLoader#load(java.lang.Object)
   */
  @Override
  public MarketConfig load(Document document) throws ConfigException {
    
    MarketConfig marketConfig = new MarketConfig();
    AppleConfig appleConfig = new AppleConfig();
    EggConfig eggConfig = new EggConfig();
    /**
     * 在這里處理document,然后就能得到
     * AppleConfig和EggConfg
     * 
     * 此處代碼省略
     */
    marketConfig.setAppleConfig(appleConfig);
    marketConfig.setEggConfig(eggConfig);
    
    /**
     * 由于OwnerConfig是需要properties方式來加載,不是xml
     * 所以這里要新建一個OwnerConfigLoader,委托它來加載OwnerConfig
     */
    
    OwnerConfigLoader ownerConfigLoader = new OwnerConfigLoader(ConfigProviderFactory.createPropertiesProvider(YOUR_FILE_PATH));
    OwnerConfig ownerConfig = ownerConfigLoader.load();
    
    marketConfig.setOwnerConfig(ownerConfig);
    
    return marketConfig;
  }
}

然后,我們在應用層面如何獲取到MarketConfig呢

MarketConfigLoader marketConfigLoader = new MarketConfigLoader(ConfigProviderFactory.createDocumentProvider(YOUR_FILE_PATH));
MarketConfig marketConfig = marketConfigLoader.load();
也許有個地方會人奇怪,明明有四個配置類,為什么只有2個配置加載器呢。
因為MarketConfig、EggConfig和AppleConfig,都是從同一個xml配置文件里面加載,所以只要一個Document對象,通過MarketConfigLoader就可以全部加載。

而OwnerConfig是不同的加載方式,所以需要另外一個加載器。

尾聲

本文提出的配置加載機制,并不能夠?qū)嶋H幫忙加載配置,這事應該留給DOM,SAX,以及其他一些開源庫如dom4j,Digester去做。
但本文提出的配置加載機制能夠讓配置加載機制更靈活,容易擴展,并且能夠集成多種配置加載方式,融合到一個機制進來,發(fā)揮各自有點。

實際上,有些軟件經(jīng)常需要同時從多種不同格式的配置文件里面加載配置,例如struts2,以及我最近在研究并被氣到吐血的某國產(chǎn)開源數(shù)據(jù)庫中間件軟件,
如果沒有一套完整的配置加載機制,那么代碼會比較散亂,可維護性不高。容易使人吐血。

相關(guān)文章

  • springboot實現(xiàn)全局異常捕獲的使用示例

    springboot實現(xiàn)全局異常捕獲的使用示例

    任何系統(tǒng),我們不會傻傻的在每一個地方進行異常捕獲和處理,整個系統(tǒng)一般我們會在一個的地方統(tǒng)一進行異常處理,本文主要介紹了springboot實現(xiàn)全局異常捕獲的使用示例,感興趣的可以了解一下
    2023-11-11
  • 淺析從同步原語看非阻塞同步以及Java中的應用

    淺析從同步原語看非阻塞同步以及Java中的應用

    非阻塞同步是基于沖突檢測的樂觀并發(fā)策略,這種樂觀的并發(fā)策略使得很多線程不需要因為競爭失敗直接掛起,這種同步措施稱為非阻塞同步。下面我們就從硬件原語開始了解非阻塞同步,并看一看在Java中非阻塞同步的一些應用
    2021-06-06
  • JAVA設計模式之組合模式原理與用法詳解

    JAVA設計模式之組合模式原理與用法詳解

    這篇文章主要介紹了JAVA設計模式之組合模式,簡單說明了組合模式的原理,并結(jié)合實例分析了java組合模式的具體用法,需要的朋友可以參考下
    2017-08-08
  • Java抽象類和接口使用梳理

    Java抽象類和接口使用梳理

    對于面向?qū)ο缶幊虂碚f,抽象是它的一大特征之一,在?Java?中可以通過兩種形式來體現(xiàn)OOP的抽象:接口和抽象類,下面這篇文章主要給大家介紹了關(guān)于Java入門基礎之抽象類與接口的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • SpringBoot登錄驗證碼實現(xiàn)過程詳解

    SpringBoot登錄驗證碼實現(xiàn)過程詳解

    這篇文章主要介紹了SpringBoot登錄驗證碼實現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • JAVA集成Freemarker生成靜態(tài)html過程解析

    JAVA集成Freemarker生成靜態(tài)html過程解析

    這篇文章主要介紹了JAVA集成Freemarker生成靜態(tài)html過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • Spring Boot實現(xiàn)簡單的定時任務

    Spring Boot實現(xiàn)簡單的定時任務

    這篇文章主要給大家介紹了關(guān)于利用Spring Boot實現(xiàn)簡單的定時任務的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者使用Spring Boot具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2020-07-07
  • Javaweb mybatis接口開發(fā)實現(xiàn)過程詳解

    Javaweb mybatis接口開發(fā)實現(xiàn)過程詳解

    這篇文章主要介紹了Javaweb mybatis接口開發(fā)實現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • 一文了解Java?線程池的正確使用姿勢

    一文了解Java?線程池的正確使用姿勢

    線程池在平時的工作中出場率非常高,基本大家多多少少都要了解過,可能不是很全面,本文和大家基于jdk8學習下線程池的全面使用,以及分享下使用過程中遇到的一些坑,希望對大家有所幫助
    2022-10-10
  • springboot項目中添加自定義日志及配置過程

    springboot項目中添加自定義日志及配置過程

    這篇文章主要介紹了springboot項目中添加自定義日志,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07

最新評論