Prop是一個超級properties;包含了很多jdk缺失的東西:utf-8支持,宏,分區,profiles,全配置等等。
屬性存儲在一個或者多個*.props文件,而且它是開放的,支持多種類型的資源。更重要的是,它和java properties是兼容的。
Props的目標不是提供一個最大限度地配置解決方案,而是提供一個比java properties更好的選擇。因此如果在應用中使用到properties,考慮使用Props來替代。
基本規則
下面是介紹props文件格式的一組基本規則。其中的一些規則如下示例:
utf8編碼
默認情況下,props文件是utf8編碼,也可以使用別的任意編碼格式。不管使用哪種編碼格式,props加載java properites時仍然使用ISO 8859-1.
去除空白字符
分區名稱和屬性名稱的開頭結尾的空白字符將被去除掉,而屬性值也會去除開頭和結尾的空白字符。
賦值
可以使用"="或者":"進行賦值。
值連接
使用"+="連接同名屬性的值(以逗號分隔)。
注解
從";"或者"#"開頭到行的結尾來注解。
轉義符
使用"\"作為轉義符,對字符進行轉義(例如 "\#"代表了字符"#","\\"代表"\")。
多行的值(Multi-line values)
如果"\"作為行的連接符,表明下一行仍然是屬性的值。
特殊字符
\\uXXXX將被編譯為字符,同樣\t,\r,\f也會編譯為字符。
多行值(Multiline values)
使用三引號作為一種方便的形式來定義多行的值。
注:三引號的語法是一對連續的單引號或者雙引號(通常都是成對的用)。
三引號讓程序員從引號和特殊字符串的泥潭里面解脫出來,自始至終保持一小塊字符串的格式是所謂的WYSIWYG(所見即所得)格式的。
基本用法
Props的使用非常簡單。概況的說,Props管理着屬性。
Props p = new Props(); p.load(new File("example.props")); ... String story = p.getValue("story");
Props加載屬性的方法有多種形式:文件、輸入流、字符串、屬性Properties。然后通過Props的etValue方法來調用屬性值,這個方法通常返回一個字符串的值。
分區
分區和INI文件的分區非常相似。在Props中,分區僅表示前綴相同的一組鍵,這組鍵直到分區結尾或者文件結尾。
分區名稱已[開頭,以]結尾。屬於該分區的屬性在分區的頭部之后。分區名稱將作為前綴加到分區屬性中。分區以空的[]結尾或者新的分區開始或者到文件尾部。
示例如下:
[users.data] weight = 49.5 height = 87.7 age = 63 [] comment=this is base property
下面的和上述示例等同:
users.data.weight = 49.5 users.data.height = 87.7 users.data.age = 63 comment=this is base property
因此,分區可以縮短文件,從而使文件更可讀。
Profiles
通常情況下,一個應用有不同的環境,因此要求不同組別的屬性。例如web應用的測試環境、發布環境。一種組織這些屬性的方法是定義不同的profile,在這些profile里同樣的鍵有不同的值。
Props支持屬性的Profile。Profile定義在鍵名內:profile名稱以<>包裝。一個可以可以有一個或者多個profile定義。同樣,profile可以定義在鍵名的任意地方,甚至在單詞的中間;然而,最好還是把它放到鍵名的尾部。
沒有profile的屬性是基於屬性的,若檢索特定profile的屬性失敗,將會檢查基本的屬性。
profile可以看着相同屬性組的一個“不同的視圖”或者“快照”。示例:
db.port=3086 db.url<develop>=localhost db.username<develop>=root db.url<deploy>=192.168.1.101 db.username<deploy>=app2499
在上面的示例中,定義了3個鍵,兩個鍵擁有兩個不同的profile(develop/deploy)且沒有基本的值。
上文提到,分區是一個鍵的前綴定義,並且profile可以定義在鍵名的任意地方,因而分區名稱也可以包含profile定義,上述的示例可以改造如下:
db.port=3086 [db<develop>] url=localhost username=root [db<deploy>] url=192.168.1.101 username=app2499
當檢索上述值時,可以指定活躍的profile:
String url = props.getValue("db.url", "develop"); String user = props.getValue("db.username", "develop");
注意:一個profile可以同時定義多次。profile的順序非常重要!當鍵定義到多個活躍的profile時,將返回第一個的值(第一個匹配的profile的值)。
你也可以僅僅檢索基本屬性(忽略profile)--使用getBaseValue()方法。基本屬性不屬於任何profile。
默認的活躍profile
通常,在一個應用的生命周期中,只有一組profile是活躍的。不需要每次在調用getValues()時都傳遞此活躍的profile。Props支持在外部定義這個所謂的活躍profile,這個定義在加載屬性的props文件中。
在使用getValue(String)檢索屬性時,活躍的profile就是默認的profile。活躍profile以@profiles的特殊屬性鍵定義。例如:
key1=hello key1<one>=Hi! @profiles=one
則下面的java代碼:
String value = props.getValue("key1");
將會返回值“Hi!”,因為活躍profile是"one".
活躍profile也可以通過java代碼設置,方法是:setActiveProfiles()
.
內部profile
有這樣一種場景:兩個或者多個profile共享大部分配置,而只有很少一部分屬性是不同的。為避免在每個profile中重復定義所有的屬性,可以使用內部profile定義哪些不同的屬性。Props首先檢索內部profile的鍵,然后檢索基本屬性,示例如下:
key1<one>=Hi! key2<one>=... .... key100<one>=... key1<one.two>=Hola!
上述示例定義了兩個profiles,第一個名稱為"one",包含了100個鍵值對。第二個profile是一個名稱為one.two的內部profile。它包含一個屬性(key1)---但上層profile所有屬性都是可用的!當使用java代碼調用上述屬性:props.getValue("key1", "one.two")時將會怎樣呢?
Props將會
在內部名為one.two的profile中檢索屬性
如果沒有找到,Props檢查上一層的profile:one。
如果還檢索不到,沒有更上一層的profile了,Props檢索基本屬性。
內部profile可用有很多層。
宏
Props最大的優點是支持宏。宏是一些鍵值的引用,別的鍵可用使用該值。宏以{}包裝。示例如下:
key1=Something ${foo}
...
foo=nice
key1的值是“Something nice”。宏可用引用任意存在的屬性鍵,不管它們定義在哪里。
同樣也支持嵌套的宏,示例如下:
key1=**${key${key3}}** key3=2 key2=foo
key1的值是"**foo**".
宏和profile
宏通常使用當前活躍或者提供的profile來解析。若當前的profile改變了,宏的值通常也會改變。
這種行為由標志位useActiveProfilesWhenResolvingMacros來控制。例子如下:
root=/app root<foo>=/foo data.path=${root}/data
data.path的值在profile設置為active時是多少?因為foo是活躍的,root的值變為/foo,因此data.path的值為/foo/data.
若我們關閉所有的profile而只使用基本屬性,data.path的值時/app/data.
也可以顯示的設置宏的profile:
root=/app root<foo>=/foo data.path=${root<foo>}/data
上述例子中,宏root將一直使用foo profile而不管當前選定的profile,因而data.path的值將一直是"/foo/data".
多行值
多行的值可以通過三引號定義。中間的都看做值:
email.body=''' Hello $n, welcome! '''
注意:多行的值的空白將不會對剪短!因此上述示例中的值將包含5行。
遍歷和鍵的順序
Props中的鍵是有順序的!因此可以根據屬性文件中鍵的順序遍歷所有的鍵。不用如下代碼:
foo.1=value1 foo.2=value2 ...
你可以遍歷props如下:
Props props = .... Iterator<PropsEntry> it = p.iterator();
遍歷的順序和props定義的順序一致。
更進一步,可以通過增加profile來過濾檢索或者/和分區遍歷。你可以這樣寫:
Iterator<PropsEntry> it = p.entries() .section("one.two") .profile("prof1", "prof2") .iterator();
上述代碼僅僅遍歷給定分區和給定profile的屬性。
由於引入了profile,可以將一個鍵定義在屬性文件的多個地方。例如,你可以為兩個不同的profile定義一個特定的值。在這種情況下,將不能確定鍵的正確順序:是第一個檢索到的鍵還是它所在位置獲取的值?你可以通過使用skipDuplicatesByValue和skipDuplicatesByPosition()來控制。
復制操作符
假定你有一定數量的屬性,即默認情況下,和不同種類的數目一樣,示例如下:
com.jodd.action1=value1 com.jodd.action2=value2 ... org.jodd.action1=value1 org.jodd.action2=value2 ... net.jodd.... # etc
Props支持你使用復制操作符(<=)來最小化重復的屬性,上述屬性可以寫成如下形式:
[actions] action1=value1 action2=value2 ... [] org.jodd <= actions [com] jodd <= actions [net.jodd] <= actions
上述示例顯示了使用復制操作的三種不同方法,沒有使用分區,使用部分分區和完全使用分區。使用這三種方法的哪一種都可以,你可以任意選擇一種。
注意:復制的值設置為宏,因此上述所有復制的屬性也等同於:
org.jodd.action1=${actions.action1} com.jodd.action1=${actions.action1} ....
配置
使用幾種配置設置可以很好的對Props進行調優:
新行轉義值
當行結尾符(EOL)需要轉義時,指定一個新的字符串。默認值是空的字符串,故多行的值和單行的值連接起來。如果新行轉義值被設置為,例如"\n"時,多行的值將以多行的形式保存。
截左側值
如果需要從左邊剪斷的話,設置此值。
截右側值
如果需要從右邊剪斷的話,設置此值。
新行忽略前面的空白值
若值分割為多行(以轉義EOL)時需要忽略主要的空白值時定義此值。默認值時true,因此下面的多行屬性:
key1=line1\
line2\
line3
將被讀成line1line2line3(連接成的)
跳過空的屬性
跳過空的屬性的標志位。
連接重復的屬性
一旦設置,重復的屬性鍵將不會覆蓋舊的鍵,而是連接並且通過逗號分隔。
多行值
默認啟用,利用一種更便利的方式來書寫多行的值,以三引號(和python一樣)來書寫。三引號內的所有字符認作一個值,因此新行不需要轉義。
源碼如下:
/** * Value that will be inserted when escaping the new line. */ protected String escapeNewLineValue = StringPool.EMPTY; /** * Trims left the value. */ protected boolean valueTrimLeft = true; /** * Trims right the value. */ protected boolean valueTrimRight = true; /** * Defines if starting whitespaces when value is split in the new line * should be ignored or not. */ protected boolean ignorePrefixWhitespacesOnNewLine = true; /** * Defines if multi-line values may be written using triple-quotes * as in python. */ protected boolean multilineValues = true; /** * Don't include empty properties. */ protected boolean skipEmptyProps = true;
參考文獻:
http://jodd.org/doc/props.html