我以爲 Xml 做爲配置文件最大的好處是結構化,對程序配置信息能夠有結構有組織的進行描述。可是使用 Xml 做爲配置文件帶給咱們的問題是咱們須要程序本身去解析Xml配置文件結構。這顯得有點多餘且囉嗦。 java
所以一般咱們的配置文件都使用 java 提供的屬性文件,屬性文件雖然沒有 Xml 文件那樣清晰的結構化,但只要標註好註釋屬性文件仍是能夠勝任這份工做的。 node
Hasor 使用 Xml 做爲其配置文件,可是能夠像屬性文件同樣去訪問它。 web
首先 Hasor 會將 Xml 片斷映射成一組 Map 的 key/value 鍵值對,以下: sql
<?xml version="1.0" encoding="UTF-8"?> <config xmlns="http://project.hasor.net/hasor/schema/main"> <!-- Demo 項目源碼所處包 --> <hasor debug="false"> <loadPackages>net.test.project.*</loadPackages> </hasor> </config>
hasor = <...> hasor.debug = <...> hasor.loadPackages = <...>
你們可能有點摸不清頭腦是怎樣一種規則,下面我就來介紹一下 Hasor 配置文件的映射規則。 數據庫
以 XPath 爲例,當咱們須要訪問"<hasor>"節點時候,可使用「hasor」這個表達式。若是須要獲取"<loadPackages>"節點的話使用「hasor/loadPackage」這個表達式。 app
Hasor 的規則與其類似。Hasor 在創建節點映射關係時會將其 XPath 做爲生成 key 的依據。不一樣於 XPath 的是 hasor 使用「.」分割。例如:loadPackage節點的映射關係是「hasor.loadPackage」。 框架
若是要想取得 Xml 配置文件中 debug 這個屬性的值,一般 Xpath 的表達式爲「hasor/@debug」,在 hasor 中屬性的映射會變爲:「hasor.debug」。或許有的同窗會以爲若是有一個名爲「debug」的 xml 元素位於 hasor 元素下豈不是會出現衝突? 分佈式
沒錯在 Hasor 的映射規則下會出現衝突,並且只會有一個有效。Hasor 設計統一配置文件讀取策略的目的並非爲了取代 XPath,而是提供一種便捷的 Xml 配置文件獲取方式。 工具
舉一個比較複雜的例子的映射關係,下面這段 Xml 取自Demo程序的配置文件,它配置了Demo程序使用的數據庫鏈接信息: 性能
<?xml version="1.0" encoding="UTF-8"?> <config xmlns="http://project.hasor.net/hasor/schema/main"> <!-- 數據源配置 --> <hasor-jdbc> <dataSourceSet default="localDB"> <!-- 名稱爲 localDB 的內存數據庫,數據庫引擎使用 HSQL --> <dataSource name="localDB" dsFactory="net.hasor.plugins.datasource.factory.C3p0Factory"> <driver>org.hsqldb.jdbcDriver</driver> <url>jdbc:hsqldb:mem:aname</url> <user>sa</user> <password></password> </dataSource> </dataSourceSet> </hasor-jdbc> </config>
對於上面這段 Xml 若是我要取得數據庫鏈接的用戶名"sa",須要使用這個映射 key:「hasor-jdbc . dataSourceSet . dataSource . user」,能夠看到取得某個元素的值其實就是經過元素名一路「.」下去的。
若是我要取得的是 dsFactory 屬性信息,可使用這個映射 key:「hasor-jdbc . dataSourceSet . dataSource . dsFactory」。
你們能夠看出,其實 Hasor 在映射時 xml 屬性會被認爲是元素的子元素,換句話說,上面這段配置文件能夠改成下面這種寫法:
<?xml version="1.0" encoding="UTF-8"?> <config xmlns="http://project.hasor.net/hasor/schema/main"> <hasor-jdbc> <dataSourceSet> <default>localDB</default> <dataSource> <name>localDB</name> <dsFactory>net.hasor.plugins.datasource.factory.C3p0Factory</dsFactory> <driver>org.hsqldb.jdbcDriver</driver> <url>jdbc:hsqldb:mem:aname</url> <user>sa</user> <password></password> </dataSource> </dataSourceSet> </hasor-jdbc> </config>
上面介紹了 Hasor 的配置文件映射規則,下面解釋一下 Hasor 配置文件讀取機制。
初一看可能有的同窗會以爲,Hasor 在讀取配置文件時或許採用了 XPath 表達式進行讀取。若是遇到頻繁訪問時性能會極具降低。
我能夠告訴你們,Hasor 考慮到 XPath 性能的問題,所以它內部並非經過 XPath 進行解析,而是使用 Map進行映射。
Hasor 在啓動時會調用 Sax 方式掃描整個 Xml 文件,而後在掃描過程當中持續的生成 Key/Value 鍵值對,這些鍵值對最後會保存到一個 Map 中,咱們取得的全部配置文件信息最後都會在這個Map中去查找。
正由於這種方式 Hasor 的配置文件讀取速度不會輸給屬性文件。
因爲 Hasor 的映射規則 Hasor 的配置文件在生成最終 Map 時會產生 key 覆蓋問題。前面已經提到了一種覆蓋現象,屬性名和子元素名同名狀況下就會出現覆蓋。
此外同名子元素也會存在 key 覆蓋問題,這些問題是 hasor 映射規則沒法解決的。而且 Hasor 的映射規則也不會力爭去解決這個問題,由於 Hasor 的配置映射規則並不打算成爲另一個 XPath。
覆蓋問題雖然會出現,可是 Hasor 卻提供了一種辦法解決它。
即便 Hasor 經過 Map 方式創建的映射,而且也是經過 Map 方式獲取節點。可是這並不能阻攔 Haso 爲您提供一套 DOM 方式獲取內容的途徑。
各位或許以爲經過 DOM 方式會引起 Hasor 重載配置文件,或者說 Hasor 內部除了 Map 映射還保留了另一個 DOM樹。那麼您就大錯特錯了!
Hasor 存儲 Xml 映射信息只有 Map 這一種途徑,Hasor 不會浪費多餘的內存空間。Hasor 在保存映射時它爲每個 Xml 映射節點都使用 XmlNode 接口進行封裝。
大到一個 Xml 元素,小到一個 Xml 屬性。凡是 Xml 中可到達的元素都是經過這個對象進行封裝。
若是 XmlNode 表示的是一個元素,那麼這個對象的 children 屬性中就保存了它的全部子節點,而 arrMap 屬性保存的就是這個元素的全部屬性。所以咱們只要經過映射 key 取得 XmlNode 接口便可。
這樣一來就能夠在不增長內存開銷的狀況下增長 DOM 方式讀取配置文件。
例以下面這個 Xml 例子:
<?xml version="1.0" encoding="UTF-8"?> <config xmlns="http://project.hasor.net/hasor/schema/main"> <demoProject> <menus> <menu code="FunA" name="功能演示A" url="/funa" /> <menu code="FunB" name="功能演示B" url="/funb" /> <menu code="UserMgr" name="用戶管理" url="/mgr/user/userList.do" /> </menus> </demoProject> </config>
解析上面這段 Xml 將它的 menu 元素信息保存到 List 中:
List<MenuBean> menuList = new ArrayList<MenuBean>(); /*獲取操縱配置文件的接口*/ Settings setting = appContext.getSettings(); /*取得‘/demoProject/menus’ Xml節點*/ XmlNode xmlNode = setting.getXmlProperty("demoProject.menus"); /*使用 DOM 方式解析 Xml節點*/ List<XmlNode> menus = xmlNode.getChildren("menu"); for (XmlNode node : menus) { MenuBean menuBean = new MenuBean(); menuBean.setCode(node.getAttribute("code")); menuBean.setName(node.getAttribute("name")); menuBean.setUrl(node.getAttribute("url")); menuList.add(menuBean); }
Hasor 的配置文件默認是不映射根節點的,若是開發者須要獲取根節點則須要經過映射 key「.」取得。當配置文件中存在多個命名空間定義狀況下,根節點也會出現多個。換句話說根節點是針對 Xml 命名空間的而非 Xml 文件內容自己。
Hasor 要求被解析的 Xml 文件必須有一個命名空間,命名空間地址能夠是任意的。這就意味着您能夠同時裝載兩個不一樣命名空間下的 Xml 配置文件,而且隨意讀取它們。Hasor 目前已經在使用的命名空間以下:
Hasor 的 Xml 解析支持多個命名空間會給程序帶來很大的意義。比方說咱們爲每一個不一樣的業務模塊都設立一個命名空間,而後不一樣業務模塊的開發交給不一樣的開發小組完成。最後在合併這些開發小組所產生的配置文件。
一般咱們會統一維護配置文件,誰修改了配置文件他就立刻遞交。後來者在修改時須要及時更新一下看看是否有更改。當開發小組成員比較集中的時候,這種辦法勉強是不會出現問題的。
若是開發小組的成員不常常在一塊兒,這種統一式的管理變得不那麼靠譜。尤爲在異地開發模式中顯得更加突出。即便是經過屬性文件維護程序的配置文件也會面對這個問題。
在這種場景下,通常咱們可能會寫好多個屬性配置文件。而後在分別用不一樣的 map 載入它們統一管理。雖然能夠解決問題,可是我以爲這並不優雅。
Hasor 的配置文件能夠經過命名空間對業務模塊加以區分,不一樣的業務模塊當使用配置文件時從本身的命名空間中取值,不影響其它模塊。這樣一來既能能夠很好控制衝突的發生,又能統一維護配置文件。
一般通常狀況下一個模塊一般也是由一個小組來完成,這樣更加能夠確保衝突的發生機率。重而很好的知足 分佈式開發 的需求。
例以下面配置文件代碼:
<?xml version="1.0" encoding="UTF-8"?> <config xmlns:mod1="http://mode1.myProject.net" xmlns:mod2="http://mode2.myProject.net" xmlns="http://project.hasor.net/hasor/schema/main"> <!-- mode1 配置 --> <mod1:config> <mod1:appSettings> <mod1:serverLocal mod1:url="www.126.com" /> </mod1:appSettings> </mod1:config> <!-- mode2 配置 --> <mod2:config> <mod2:appSettings> <mod2:serverLocal mod2:url="www.souhu.com" /> </mod2:appSettings> </mod2:config> </config>
在這個配置文件中實際上是定義了三個命名空間,之因此這樣寫的目的是爲了看起來更加直觀。下面是測試代碼:
File inFile = new File("ns-config.xml"); FileSettings settings = new FileSettings(inFile); // //Mode1 Settings mod1 = settings.getSetting("http://mode1.myProject.net"); Settings mod2 = settings.getSetting("http://mode2.myProject.net"); System.out.println(mod1.getString("appSettings.serverLocal.url")); System.out.println(mod2.getString("appSettings.serverLocal.url"));
在多個命名空間下 Hasor 的配置文件解析會爲每一個命名空間的元素都創建一個根節點。
爲了更加方便協做開發,Hasor 的 Xml 配置文件解析機制還支持多個配置文件同時加載。
加載的不一樣配置中文件會出現 衝突問題 ,Hasor 當遇到衝突時採用的 內容覆蓋&合併 機制。
例如:若是遇到相同映射 key 的元素,Hasor 會使用新的元素內容覆蓋已存在的元素內容。新元素的子元素和屬性會按照順序追加到已有老元素的 children 和 attrMap 中。
可是這種合併不會將兩個不一樣命名空間下的衝突元素合併到一塊兒。它們會被保存在彼此隔離的兩個不一樣 Map 中。這個 Map 是在映射 key/value 鍵值對的上層創建的一個命名空間 Map。這個結構是Map<String,Map<String,String>> 結構。
因此說若是加載的兩個配置文件雖然有相同的屬性映射 key ,但處於不一樣命名空間下。Hasor 的配置文件加載不會認爲它們是衝突的,您使用上面的代碼仍然能夠讀取到位於兩個配置文件中的不一樣配置。例如:
------ns1-config.xml <?xml version="1.0" encoding="UTF-8"?> <config xmlns="http://mode1.myProject.net"> <appSettings> <serverLocal url="www.126.com" /> </appSettings> </config> ------ns2-config.xml <?xml version="1.0" encoding="UTF-8"?> <config xmlns="http://mode2.myProject.net"> <appSettings> <serverLocal url="www.souhu.com" /> </appSettings> </config>
程序代碼以下:
File inFile1 = new File("ns1-config.xml"); File inFile2 = new File("ns2-config.xml"); FileSettings settings = new FileSettings(); settings.addFile(inFile1); settings.addFile(inFile2); settings.refresh(); // Settings mod1 = settings.getSetting("http://mode1.myProject.net"); Settings mod2 = settings.getSetting("http://mode2.myProject.net"); System.out.println(mod1.getString("appSettings.serverLocal.url")); System.out.println(mod2.getString("appSettings.serverLocal.url"));
Hasor 的配置文件按照做用分爲兩種,它們分別用於不一樣的場景。static-config.xml 配置文件一般是伴隨hasor軟件包一同發佈的。您能夠在:Hasor-Core、Hasor-Web、Hasor-JDBC 它們的 jar 包中找到它們的身影。
「static-config.xml」顧名思義靜態配置,意思就是說這些配置文件內容是不能夠被改變的。且文件名也是固定的,這也包括了它們的存放位置。
一般靜態配置文件中保存的是默認配置,在軟件開發中通常不會涉及到編寫一個「static-config.xml」。固然若是您的軟件中有不少配置信息不想暴露給實施人員,能夠考慮將這些配置項目放到static-config.xml中。
Hasor 在啓動時候會先加載位於 classpath 中全部指定目錄中的 static-config.xml,而且按照前面說的邏輯進行處理。static-config.xml 的加載順序取決於 jar 包掃描順序,因爲具體環境的複雜性,您能夠認爲它們的加載順序是不可靠的。
在接觸 Hasor 的配置文件時,常常被說起的是 hasor-config.xml 。它被成爲主配置文件,做爲主配置文件它的內容是最高優先級的。它的加載是在 static-config.xml 加載完成以後,這樣能夠確保 hasor-config.xml 的內容覆蓋默認配置。
此外,Hasor 的配置文件系統還會監控主配置文件是否在運行期發生了改變。若是發生改變,Hasor 會重載這個配置文件,而後經過 SettingsListener 接口通知配置文件重載了。
開發者可使用 SettingsListener 接口開發出動態修改配置文件內容而讓程序自動感知的效果,Hasor-Core 中絕大部分配置都是支持 及時修改及時生效的。而 Hasor-Web 和 Hasor-JDBC 中的配置卻不支持及時修改及時生效。
圖中藍色部分表示接口,灰色部分表示類。Hasor 的整個配置文件服務類的層次關係如上圖所示。
AbstractSettings 該類主要的目的是抽象出 key/value 數據容器(Map)。並基於這個容器實現大部分 Settings 接口中的方法。
AbstractBaseSettings 抽象類是基於 AbstractSettings 的功能對數據容器提供了命名空間區分支持,該類實現了 getSetting(namespace) 接口方法和 getSettingArray 接口方法。
InputStreamSettings 類是全部基於流形式加載配置文件的基類,該類實現了 IOSettings 接口。而且經過 loadSettings 方法實現配置文件加載。也是該類提供了基於 Sax 下的 Xml 配置文件解析支持,這部分代碼位於 SaxXmlParser 類中。該類還支持多個 InputStream 的順序解析。
FileSettings 是一個獨立的 Hasor 配置文件解析服務類,第六小節已經展現瞭如何使用它。
StandardContextSettings 該類是 Hasor 配置文件體系所使用的工具類,該類提供了「static-config.xml」和「hasor-config.xml」配置文件的加載支持。
第7小節所闡述的配置文件修改監聽器功能,是由 Environment 部分提供的,本文暫不說起。
-------------------------------
以上就是 Hasor 配置文件解析的主要內容,因爲篇幅的關係。有關擴展部分留在之後在介紹。
在最後,不少同窗看完以後可能以爲 Hasor 的配置文件體系,彷佛有點超越配置文件自己所作的事情。的確是這樣,由於 Hasor 須要爲開發者提供基於 Xml 下統一的最簡化的配置文件解析服務。所以不少方面都須要考慮周全,所以纔有的這個規模。
最後奉獻上 Hasor 配置文件接口的定義供你們欣賞,最後祝你們工做愉快,元旦快樂。
public interface Settings { //在框架掃描包的範圍內查找具備特徵類集合。(特徵能夠是繼承的類、標記某個註解的類) public Set<Class<?>> getClassSet(Class<?> featureType, String[] loadPackages); //在框架掃描包的範圍內查找具備特徵類集合。(特徵能夠是繼承的類、標記某個註解的類) public Set<Class<?>> getClassSet(Class<?> featureType, String loadPackages); //獲取指在某個特定命名空間下的Settings接口對象。 public String[] getSettingArray(); //獲取指在某個特定命名空間下的Settings接口對象。 public Settings getSetting(String namespace); //強制從新裝載配置文件。 public void refresh() throws IOException; // //解析全局配置參數,而且返回其{@link Character}形式對象。 public Character getChar(String name); //解析全局配置參數,而且返回其{@link Character}形式對象。第二個參數爲默認值。 public Character getChar(String name, Character defaultValue); //解析全局配置參數,而且返回其{@link String}形式對象。 public String getString(String name); //解析全局配置參數,而且返回其{@link String}形式對象。第二個參數爲默認值。 public String getString(String name, String defaultValue); //解析全局配置參數,而且返回其{@link Boolean}形式對象。 public Boolean getBoolean(String name); //解析全局配置參數,而且返回其{@link Boolean}形式對象。第二個參數爲默認值。 public Boolean getBoolean(String name, Boolean defaultValue); //解析全局配置參數,而且返回其{@link Short}形式對象。 public Short getShort(String name); //解析全局配置參數,而且返回其{@link Short}形式對象。第二個參數爲默認值。 public Short getShort(String name, Short defaultValue); //解析全局配置參數,而且返回其{@link Integer}形式對象。 public Integer getInteger(String name); //解析全局配置參數,而且返回其{@link Integer}形式對象。第二個參數爲默認值。 public Integer getInteger(String name, Integer defaultValue); //解析全局配置參數,而且返回其{@link Long}形式對象。 public Long getLong(String name); //解析全局配置參數,而且返回其{@link Long}形式對象。第二個參數爲默認值。 public Long getLong(String name, Long defaultValue); //解析全局配置參數,而且返回其{@link Float}形式對象。 public Float getFloat(String name); //解析全局配置參數,而且返回其{@link Float}形式對象。第二個參數爲默認值。 public Float getFloat(String name, Float defaultValue); //解析全局配置參數,而且返回其{@link Double}形式對象。 public Double getDouble(String name); //解析全局配置參數,而且返回其{@link Double}形式對象。第二個參數爲默認值。 public Double getDouble(String name, Double defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象。 public Date getDate(String name); //解析全局配置參數,而且返回其{@link Date}形式對象。第二個參數爲默認值。 public Date getDate(String name, Date defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象。第二個參數爲默認值。 public Date getDate(String name, long defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象。 public Date getDate(String name, String format); //解析全局配置參數,而且返回其{@link Date}形式對象。第二個參數爲默認值。 public Date getDate(String name, String format, Date defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象。第二個參數爲默認值。 public Date getDate(String name, String format, long defaultValue); //解析全局配置參數,而且返回其{@link Enum}形式對象。第二個參數爲默認值。 public <T extends Enum<?>> T getEnum(String name, Class<T> enmType); //解析全局配置參數,而且返回其{@link Enum}形式對象。第二個參數爲默認值。 public <T extends Enum<?>> T getEnum(String name, Class<T> enmType, T defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象(用於表示文件)。第二個參數爲默認值。 public String getFilePath(String name); //解析全局配置參數,而且返回其{@link Date}形式對象(用於表示文件)。第二個參數爲默認值。 public String getFilePath(String name, String defaultValue); //解析全局配置參數,而且返回其{@link File}形式對象(用於表示目錄)。第二個參數爲默認值。 public String getDirectoryPath(String name); //解析全局配置參數,而且返回其{@link File}形式對象(用於表示目錄)。第二個參數爲默認值。 public String getDirectoryPath(String name, String defaultValue); //解析全局配置參數,而且返回其{@link XmlNode}形式對象。 public XmlNode getXmlProperty(String name); // //解析全局配置參數,而且返回其{@link Character}形式對象。 public Character[] getCharArray(String name); //解析全局配置參數,而且返回其{@link Character}形式對象。第二個參數爲默認值。 public Character[] getCharArray(String name, Character defaultValue); //解析全局配置參數,而且返回其{@link String}形式對象。 public String[] getStringArray(String name); //解析全局配置參數,而且返回其{@link String}形式對象。第二個參數爲默認值。 public String[] getStringArray(String name, String defaultValue); //解析全局配置參數,而且返回其{@link Boolean}形式對象。 public Boolean[] getBooleanArray(String name); //解析全局配置參數,而且返回其{@link Boolean}形式對象。第二個參數爲默認值。 public Boolean[] getBooleanArray(String name, Boolean defaultValue); //解析全局配置參數,而且返回其{@link Short}形式對象。 public Short[] getShortArray(String name); //解析全局配置參數,而且返回其{@link Short}形式對象。第二個參數爲默認值。 public Short[] getShortArray(String name, Short defaultValue); //解析全局配置參數,而且返回其{@link Integer}形式對象。 public Integer[] getIntegerArray(String name); //解析全局配置參數,而且返回其{@link Integer}形式對象。第二個參數爲默認值。 public Integer[] getIntegerArray(String name, Integer defaultValue); //解析全局配置參數,而且返回其{@link Long}形式對象。 public Long[] getLongArray(String name); //解析全局配置參數,而且返回其{@link Long}形式對象。第二個參數爲默認值。 public Long[] getLongArray(String name, Long defaultValue); //解析全局配置參數,而且返回其{@link Float}形式對象。 public Float[] getFloatArray(String name); //解析全局配置參數,而且返回其{@link Float}形式對象。第二個參數爲默認值。 public Float[] getFloatArray(String name, Float defaultValue); //解析全局配置參數,而且返回其{@link Double}形式對象。 public Double[] getDoubleArray(String name); //解析全局配置參數,而且返回其{@link Double}形式對象。第二個參數爲默認值。 public Double[] getDoubleArray(String name, Double defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象。 public Date[] getDateArray(String name); //解析全局配置參數,而且返回其{@link Date}形式對象。第二個參數爲默認值。 public Date[] getDateArray(String name, Date defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象。第二個參數爲默認值。 public Date[] getDateArray(String name, long defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象。 public Date[] getDateArray(String name, String format); //解析全局配置參數,而且返回其{@link Date}形式對象。第二個參數爲默認值。 public Date[] getDateArray(String name, String format, Date defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象。第二個參數爲默認值。 public Date[] getDateArray(String name, String format, long defaultValue); //解析全局配置參數,而且返回其{@link Enum}形式對象。第二個參數爲默認值。 public <T extends Enum<?>> T[] getEnumArray(String name, Class<T> enmType); //解析全局配置參數,而且返回其{@link Enum}形式對象。第二個參數爲默認值。 public <T extends Enum<?>> T[] getEnumArray(String name, Class<T> enmType, T defaultValue); //解析全局配置參數,而且返回其{@link Date}形式對象(用於表示文件)。第二個參數爲默認值。 public String[] getFilePathArray(String name); //解析全局配置參數,而且返回其{@link Date}形式對象(用於表示文件)。第二個參數爲默認值。 public String[] getFilePathArray(String name, String defaultValue); //解析全局配置參數,而且返回其{@link File}形式對象(用於表示目錄)。第二個參數爲默認值。 public String[] getDirectoryPathArray(String name); //解析全局配置參數,而且返回其{@link File}形式對象(用於表示目錄)。第二個參數爲默認值。 public String[] getDirectoryPathArray(String name, String defaultValue); //解析全局配置參數,而且返回其{@link XmlNode}形式對象。 public XmlNode[] getXmlPropertyArray(String name); }