Java完成設置裝備擺設加載機制。本站提示廣大學習愛好者:(Java完成設置裝備擺設加載機制)文章只能為提供參考,不一定能成為您想要的結果。以下是Java完成設置裝備擺設加載機制正文
媒介
現現在簡直年夜多半Java運用,例如我們耳熟能詳的tomcat, struts2, netty...等等數都數不外來的軟件,
要知足通用性,都邑供給設置裝備擺設文件供應用者定制功效。
乃至有一些例如Netty如許的收集框架,簡直完整就是由設置裝備擺設驅動,如許的軟件我們也平日稱之為"微內核架構"的軟件。
你把它設置裝備擺設成甚麼,它就是甚麼。
It is what you configure it to be.
最多見的設置裝備擺設文件格局是XML, Properties等等文件。
本文商量加載設置裝備擺設中最通用也是最多見的場景,那就是把一個設置裝備擺設文件映照成Java裡的POJO對象.
並商量若何完成分歧方法的加載,例如,有一些設置裝備擺設是從當地XML文件外面加載的,而有一些設置裝備擺設須要從當地Properties文件加載,更有甚者,有一些設置裝備擺設須要經由過程收集加載設置裝備擺設。
若何完成如許一個設置裝備擺設加載機制,讓我們具有這個機制後,不會讓加載設置裝備擺設的代碼分布獲得處都是,而且可擴大,可治理。
設置裝備擺設加載器
起首,我們須要一個設置裝備擺設加載器,而這個設置裝備擺設加載器是可以有多種分歧的加載方法的,是以,我們用一個接口來描寫它,以下所示:
/**
*
*
* @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> ?
很顯著,當我們要應用一個設置裝備擺設加載器時,你得告知這個設置裝備擺設加載器你須要加載後獲得甚麼成果。
例如,你願望加載設置裝備擺設後獲得一個AppleConfig對象,那末你便可以這麼去應用上述界說的接口:
IConfigLoader<AppleConfig> loader = new AppleConfigLoader<AppleConfig>(); AppleConfig config = loader.load();
因而你將設置裝備擺設文件裡的信息轉化成了一個AppleConfig對象,而且你能獲得這個AppleConfig對象實例。
到今朝,貌似只需我們的AppleConfigLoader外面完成了怎樣加載設置裝備擺設文件的詳細休息,我們便可以隨意馬虎加載設置裝備擺設了。
可以這麼說,然則不是還沒有斟酌到,設置裝備擺設能夠經由過程分歧的方法加載呢,好比經由過程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>來聲明泛型呢?
假如須要一個供給者,那末至多得告知這個供給者它該供給甚麼吧。
是以,一個供給者會供給甚麼,由這個來決議。
同時,到這裡,我們可以先建造一個工場,讓它來臨盆特定的供給者:
/**
*
*
* @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);
}
}
可以開端完成詳細設置裝備擺設加載器了?
還不可!
到這裡,假定我們有一個設置裝備擺設文件,叫apple.xml。並且我們要經由過程DOM方法把這一份apple.xml加載後釀成AppleConfig對象。
那末,起首我要經由過程供給者工場給我制作一個能供給Document的供給者。然後拿到這個供給者,我便可以挪用它的provide辦法來取得Document對象,
有了document對象,那末我便可以開端來加載設置裝備擺設了。
可是,假如要加載BananaConfig、PearConfig.......呢,其步調都是一樣的。是以我們還要有一個籠統類,來完成一些默許的配合行動。
/**
*
*
* @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;
}
}
每一個設置裝備擺設加載器都有一個帶參數結構器,吸收一個Provider。
泛型指清楚明了我要加載的是AppleConfig照樣BananConfig,泛型<U>指清楚明了要用甚麼加載方法加載,是Document呢,照樣Properties,或許其他。
實戰應用實例
有一份菜市場設置裝備擺設文件market.xml,設置裝備擺設了菜市場的商品,外面有兩種商品,分離是蘋果和雞蛋。
<market>
<apple>
<color>red</color>
<price>100</price>
</apple>
<egg>
<weight>200</weight>
</egg>
</market>
別的還有一份關於各個檔口老板名字的設置裝備擺設文件,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是分歧的加載方法,所以須要別的一個加載器。
序幕
本文提出的設置裝備擺設加載機制,其實不可以或許現實協助加載設置裝備擺設,這事應當留給DOM,SAX,和其他一些開源庫如dom4j,Digester去做。
但本文提出的設置裝備擺設加載機制可以或許讓設置裝備擺設加載機制更靈巧,輕易擴大,而且可以或許集成多種設置裝備擺設加載方法,融會到一個機制出去,施展各自有點。
現實上,有些軟件常常須要同時從多種分歧格局的設置裝備擺設文件外面加載設置裝備擺設,例如struts2,和我比來在研討並被氣到吐血的某國產開源數據庫中央件軟件,
假如沒有一套完全的設置裝備擺設加載機制,那末代碼會比擬狼藉,可保護性不高。輕易令人吐血。