国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java實(shí)現(xiàn)配置加載機(jī)制

2019-11-26 14:38:41
字體:
供稿:網(wǎng)友

前言

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

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

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

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

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

配置加載器

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

/** *  * * @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;}

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

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

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

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

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

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

配置加載方式的提供者

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

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

我們用一個(gè)接口IConfigProvider來定義這個(gè)提供者

/** * * * @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;}

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

因此,一個(gè)提供者會(huì)提供什么,由這個(gè)來決定。

同時(shí),到這里,我們可以先建造一個(gè)工廠,讓它來生產(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);  }}

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

還不行!

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

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

可是,如果要加載BananaConfig、PearConfig.......呢,其步驟都是一樣的。因此我們還要有一個(gè)抽象類,來實(shí)現(xiàn)一些默認(rè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;  }}

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

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

實(shí)戰(zhàn)運(yùn)用實(shí)例

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

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

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

port1=Steve Jobsport2=Bill Gatesport3=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);  }}

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

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,設(shè)置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     * 所以這里要新建一個(gè)OwnerConfigLoader,委托它來加載OwnerConfig     */        OwnerConfigLoader ownerConfigLoader = new OwnerConfigLoader(ConfigProviderFactory.createPropertiesProvider(YOUR_FILE_PATH));    OwnerConfig ownerConfig = ownerConfigLoader.load();        marketConfig.setOwnerConfig(ownerConfig);        return marketConfig;  }}

然后,我們在應(yīng)用層面如何獲取到MarketConfig呢

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

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

尾聲

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

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

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 射洪县| 新竹县| 景谷| 钟祥市| 黔西| 广德县| 天峻县| 丹巴县| 法库县| 青川县| 旬阳县| 兴仁县| 阜新| 巨野县| 遂平县| 西和县| 靖宇县| 万全县| 磐石市| 保康县| 临洮县| 和硕县| 家居| 镇坪县| 临澧县| 蒙阴县| 云浮市| 贵溪市| 新营市| 彝良县| 桐乡市| 玉树县| 隆昌县| 理塘县| 龙陵县| 泾川县| 衡阳市| 阳泉市| 青冈县| 柘城县| 陆川县|