第十一章:Joran
Joran 代表寒冷的西北風(fēng)恨搓,常常猛烈的吹在日列瓦湖上痹换。位于西歐中部的日列瓦湖蝗羊,表面上看起來(lái)比其它許多歐洲的湖泊都要小固棚。但是它的平均深度有 153 米统翩,異常的深。并且此洲,它是西歐最大的淡水湖厂汗。
正如前幾章所示,logback 基于 Joran呜师,一個(gè)成熟的娶桦,靈活的并且強(qiáng)大的配置框架。logback 提供的許多的功能匣掸,只能基于 Joran 來(lái)實(shí)現(xiàn)趟紊。這章將專(zhuān)注于 Joran 的基本設(shè)計(jì)以及一些明顯的特征氮双。
Joran 實(shí)際上是一個(gè)通用的配置系統(tǒng)碰酝,能夠被獨(dú)立用于日志記錄。為了強(qiáng)調(diào)這一點(diǎn)戴差,我們需要說(shuō)明的是 logback-core 模塊沒(méi)有 logger 的概念送爸。所以,本章的大多數(shù)示例與 logger暖释,appender袭厂,layout 無(wú)關(guān)。
本章節(jié)中的示例可以在 LOGBACK_HOME/logback-examples/src/main/java/chapters/onJoran/ 文件夾下被找到球匕。
要安裝 Joran纹磺,只需要下載,然后將 logback-core-1.3.0-alpha4.jar 放到類(lèi)路徑下亮曹。
歷史回顧
反射是 Java 語(yǔ)言一個(gè)強(qiáng)大的特性橄杨,使得聲明式的配置軟件系統(tǒng)變成可能秘症。例如,EJB 許多重要的屬性都被配置在 ejb.xml 文件中式矫。盡管 EJB 是用 Java 編寫(xiě)的乡摹,但是它們的許多屬性都是通過(guò) ejb.xml 來(lái)指定的。類(lèi)似的采转,logback 也可以通過(guò)指定的 XML 格式的配置文件來(lái)進(jìn)行設(shè)置聪廉。JDK 1.5 中的注解在 EJB 3.0 被大量使用用來(lái)替換之前 XML 文件中的許多指令。Joran 也會(huì)充分利用注解故慈,但是使用的范圍少的多板熊。由于 logback 配置的動(dòng)態(tài)特性 (相比 EJB),Joran 使用注解相當(dāng)有限察绷。
在 logback 它爹 log4j 中邻邮, DOMConfigurator
類(lèi)是 log4j 1.2.x 以及以后的版本的一部分。也能夠解析 XML 的配置文件克婶。DOMConfigurator
的編寫(xiě)方式強(qiáng)迫開(kāi)發(fā)人員在配置文件的結(jié)構(gòu)每次發(fā)生改變時(shí)筒严,需要重新調(diào)整代碼。調(diào)整的代碼需要重新編譯并重新部署情萤。同樣重要的是鸭蛙,DOMConfigurator
的代碼由循環(huán)組成,用于解析子元素筋岛,包含了許多 if/else 語(yǔ)句娶视。這樣的代碼散發(fā)著冗余以及重復(fù)的味道。 commons-digester 告訴我們可以通過(guò)模式匹配規(guī)則來(lái)解析 XML 文件睁宰。在解析的時(shí)候肪获,解析器會(huì)應(yīng)用匹配了指定模式的規(guī)則。規(guī)則類(lèi)通常比較小柒傻,并且具有專(zhuān)業(yè)性孝赫。因此,理解與維護(hù)相對(duì)簡(jiǎn)單红符。
有了 DOMConfigurator
的經(jīng)驗(yàn)青柄,我們開(kāi)始開(kāi)發(fā) Joran
,一個(gè)在 logback 中使用的预侯、強(qiáng)大的配置框架致开。Joran 受到了 commons-digester 項(xiàng)目很大的啟發(fā)。但是萎馅,它使用了一個(gè)稍微不同術(shù)語(yǔ)双戳。在 commons-digester 中,規(guī)則可以看做由模式和規(guī)則組成糜芳,如同 Digester.addRule(String pattern, Rule rule)
方法展示的一樣飒货。我們發(fā)現(xiàn)一個(gè)不必要的困惑是規(guī)則包含自身千诬,但是不是遞歸,而是有不同的含義膏斤。在 Joran 中徐绑,規(guī)則由模式以及動(dòng)作組成。當(dāng)相應(yīng)的模式被匹配時(shí)莫辨,會(huì)調(diào)用一個(gè)動(dòng)作傲茄。模式與動(dòng)作的這種關(guān)系是 Joran 的核心。值得注意的是沮榜,可以使用簡(jiǎn)單的模式來(lái)處理復(fù)雜的匹配盘榨,或者更確切的是說(shuō)是使用精確匹配以及通配符匹配。
SAX 還是 DOM ?
由于 SAX API 是基于事件的結(jié)構(gòu)蟆融,所以基于 SAX 的工具不能很好的處理前向引用草巡,也就是引用元素被定義晚于當(dāng)前元素被處理。循環(huán)引用元素也有同樣的問(wèn)題型酥。通常山憨,DOM API 允許用戶在所有的元素上進(jìn)行搜索,并且可以向前跳轉(zhuǎn)弥喉。
這種額外的靈活性導(dǎo)致我們?cè)陂_(kāi)始的時(shí)候選擇 DOM API 作為 Joran 的解析器郁竟。在經(jīng)過(guò)了一些實(shí)驗(yàn)之后,我們發(fā)現(xiàn)當(dāng)解析規(guī)則通過(guò)模式以及動(dòng)作表達(dá)時(shí)由境,在解析 DOM 樹(shù)時(shí)處理相隔較遠(yuǎn)的元素沒(méi)有意義棚亩。Joran 只需要 XML 文檔中連續(xù)且深度優(yōu)先順序的元素。
而且虏杰,在發(fā)生錯(cuò)誤時(shí)讥蟆,SAX API 提供元素的位置信息可以讓 Joran 去展示精確的行號(hào)與列號(hào)。位置信息在識(shí)別解析問(wèn)題時(shí)非常方便纺阔。
非目標(biāo)
考慮到它的高度動(dòng)態(tài)特性瘸彤,Joran API 沒(méi)有打算去解析包含幾千個(gè)元素的 XML 文檔。
模式 (Pattern)
Joran 的模式本質(zhì)上就是一個(gè)字符串州弟。有兩種形式的模式:exact 與 wildcard钧栖。模式 "a/b" 可以用來(lái)匹配嵌套在 <a>
元素中的 <b>
元素低零。因?yàn)?exact 匹配的設(shè)置婆翔,"a/b" 模式不會(huì)匹配其它的元素。
wildcard 可以用來(lái)進(jìn)行后綴與前綴匹配掏婶。例如啃奴,"*/a" 可以用來(lái)匹配任何以 "a" 結(jié)尾的后綴,也就是 XML 文檔中任何 <a>
元素雄妥,但是不包含任何嵌套在 <a>
中的元素最蕾。"a/*" 將會(huì)匹配任何 <a>
開(kāi)頭的元素依溯,即任何嵌套在 <a>
中的元素。
動(dòng)作
正如之前提到的瘟则,Joran 解析規(guī)則由相關(guān)聯(lián)的模式組成黎炉。動(dòng)作繼承了 Action
類(lèi),包含了如下的抽象方法醋拧。為了簡(jiǎn)單起見(jiàn)慷嗜,其它的方法被隱藏了。
package ch.qos.logback.core.joran.action;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import ch.qos.logback.core.joran.spi.InterpretationContext;
public abstract class Action extends ContextAwareBase {
/**
* 當(dāng)解析器遇到一個(gè)元素匹配
* {@link ch.qos.logback.core.joran.spi.Pattern Pattern}.
*/
public abstract void begin(InterpretationContext ic, String name,
Attributes attributes) throws ActionException;
/**
* 傳遞包含元素的 body (作為字符串) 參數(shù)
*/
public void body(InterpretationContext ic, String body)
throws ActionException {
// NOP
}
/*
* 當(dāng)解析器遇到最后一個(gè)元素匹配
* {@link ch.qos.logback.core.joran.spi.Pattern Pattern}.
*/
public abstract void end(InterpretationContext ic, String name)
throws ActionException;
}
所以丹壕,每個(gè)動(dòng)作必須實(shí)現(xiàn) begin()
與 end()
方法庆械。body()
方法的實(shí)現(xiàn)是可選的,因?yàn)?Action
提供了一個(gè)空的實(shí)現(xiàn)菌赖。
規(guī)則存儲(chǔ)
如前面提到的缭乘,根據(jù)匹配模式調(diào)用動(dòng)作是 Joran 的核心概念。規(guī)則跟模式與動(dòng)作相關(guān)聯(lián)琉用。規(guī)則被存儲(chǔ)在 RuleStore 中堕绩。
根據(jù)之前提到的,Joran 建立在 SAX API 上邑时。當(dāng) XML 文檔被解析時(shí)逛尚,每個(gè)元素會(huì)生成對(duì)應(yīng) start、body刁愿、end 的事件绰寞。當(dāng) Joran 的配置器接收到這些事件時(shí),它會(huì)根據(jù)當(dāng)前模式去規(guī)則存儲(chǔ)中查找對(duì)應(yīng)的動(dòng)作铣口。例如滤钱,元素 B 的 start、body脑题、end 事件的當(dāng)前模式為 "A/B"件缸,內(nèi)嵌在一個(gè)頂級(jí)元素 A 中。當(dāng) Joran 接收并處理 SAX 事件時(shí)叔遂,它會(huì)自動(dòng)維護(hù)當(dāng)前模式的數(shù)據(jù)結(jié)構(gòu)他炊。
當(dāng)有幾個(gè)規(guī)則匹配到當(dāng)前模式時(shí),精確匹配會(huì)比后綴匹配優(yōu)先已艰,后綴匹配會(huì)比前綴匹配優(yōu)先痊末。對(duì)于詳細(xì)的實(shí)現(xiàn)細(xì)節(jié),請(qǐng)查看 SimpleRuleStore 類(lèi)哩掺。
解析上下文
為了允許多個(gè)動(dòng)作相互協(xié)作凿叠,在調(diào)用 begin 與 end 方法時(shí)會(huì)包含解析上下文,作為第一個(gè)參數(shù)傳遞。解析上下文包含對(duì)象棧盒件,對(duì)象映射蹬碧,錯(cuò)誤列表以及 Joran 調(diào)用動(dòng)作時(shí)的一個(gè)引用。請(qǐng)查看 InterpretationContext
類(lèi)中詳細(xì)的字段列表炒刁。
動(dòng)作可以通過(guò)對(duì)象棧獲取恩沽,入棧,出棧操作翔始,或者通過(guò)對(duì)象映射來(lái)放置飒筑、獲取 key 來(lái)進(jìn)行協(xié)作。動(dòng)作可以在解析上下文的 StatusManager
上通過(guò)添加錯(cuò)誤項(xiàng)來(lái)報(bào)告任何錯(cuò)誤條件绽昏。
Hello world
這個(gè)章節(jié)中的第一個(gè)例子將會(huì)展示使用 Joran 所需要最小條件协屡。這個(gè)例子由一個(gè)名為 HelloWorldAction
的動(dòng)作組成。在調(diào)用它的 begin()
方法時(shí)會(huì)在控制臺(tái)打印 "Hello World"全谤。配置文件由解析器負(fù)責(zé)解析肤晓。為了實(shí)現(xiàn)本章的目的,我們實(shí)現(xiàn)了一個(gè)非常的簡(jiǎn)單的配置器 SimpleConfigurator
认然。HelloWorld
應(yīng)用會(huì)將下面這些結(jié)合在一起:
- 創(chuàng)建一個(gè)規(guī)則與
Context
的映射 - 創(chuàng)建一個(gè)與 hello-world 模式相關(guān)的解析規(guī)則补憾,以及對(duì)應(yīng)的
HelloWorldAction
實(shí)例 - 創(chuàng)建一個(gè)
SimpleConfigutator
,解析之前提到的規(guī)則映射卷员。 - 調(diào)用配置器的
doConfigure
方法盈匾,解析 XML 文件 - 最后,將會(huì)收集上下文中的所有轉(zhuǎn)態(tài)信息毕骡。如果有的話削饵,將會(huì)打印
hello.xml 包含一個(gè) <hello-world> 元素,沒(méi)有任何其它的內(nèi)置元素未巫。詳細(xì)的內(nèi)容請(qǐng)查看 logback-examples/src/main/java/chapters/onJoran/helloWorld/ 文件夾中的內(nèi)容窿撬。
通過(guò) hello.xml 運(yùn)行 HelloWorld 應(yīng)用將會(huì)在控制臺(tái)輸出 "Hello World"。
java chapters.onJoran.helloWorld.HelloWorld src/main/java/chapters/onJoran/helloWorld/hello.xml
強(qiáng)烈推薦你在規(guī)則存儲(chǔ)中添加新的規(guī)則叙凡,更改 XML 配置 (hello.xml)劈伴,以及添加新的動(dòng)作。
動(dòng)作相互合作
logback-examples/src/main/java/joran/calculator/ 文件夾包含了幾個(gè)動(dòng)作握爷,為了完成簡(jiǎn)單的計(jì)算跛璧,它們通過(guò)共同的對(duì)象棧相互合作。
calculator1.xml 文件包含一個(gè) computation
元素新啼,內(nèi)嵌了一個(gè) literal
元素追城。如下:
Example: calculator1.xml
<computation name="total">
<literal value="3"/>
</computation>
在應(yīng)用 Calculator1
中,我們聲明了各種解析規(guī)則 (模式與動(dòng)作) 基于 XML 文檔的內(nèi)容一起合作來(lái)計(jì)算一個(gè)結(jié)果师抄。
通過(guò) calculator1.xml 運(yùn)行 Calculator
:
java chapters.onJoran.calculator.Calculator1 src/main/java/chapters/onJoran/calculator/calculator1.xml
將會(huì)輸出:
The computation named [total] resulted in the value 3
解析 calculator1.xml 文檔包含如下的步驟:
- 開(kāi)始事件對(duì)應(yīng)的 <computation> 元素轉(zhuǎn)換為當(dāng)前模式 "/computation"漓柑。因?yàn)樵?
Calculator1
中我們?yōu)?"/computation" 模式關(guān)聯(lián)了一個(gè)ComputationAction1
實(shí)例。ComputationAction1
實(shí)例中的begin()
方法將會(huì)被調(diào)用 - 開(kāi)始事件對(duì)應(yīng)的 <litera> 元素轉(zhuǎn)換為當(dāng)前模式 "/computation/literal"叨吮。為 "/computation/literal" 關(guān)聯(lián)了一個(gè)
LiteralAction
實(shí)例辆布。LiteralAction
實(shí)例中的begin()
方法將會(huì)被調(diào)用。 - 同樣的茶鉴,結(jié)束事件對(duì)應(yīng)的 <computation> 元素將會(huì)觸發(fā)
ComputationAction1
實(shí)例中的end()
方法的調(diào)用锋玲。
有意思的是動(dòng)作相互合作的方式。LiteralAction
讀取到一個(gè)字面值涵叮,并將其放到對(duì)象棧中惭蹂,由 InterpretationContext
來(lái)維護(hù)。一旦完成割粮,其它的動(dòng)作可以獲取該值或者對(duì)其進(jìn)行更改盾碗。ComputationAction1
類(lèi)的 end()
方法從棧中獲取值,并打印舀瓢。
下一個(gè)例子中廷雅, calculator2.xml 有點(diǎn)復(fù)雜,但是更加有趣京髓。
有趣個(gè)雞兒
Example:calculator2.xml
<computation name="toto">
<literal value="7"/>
<literal value="3"/>
<add/>
<literal value="3"/>
<multiply/>
</computation>
在之前的例子中航缀,為了響應(yīng) <literal> 元素,LiteralAction
實(shí)例會(huì)將 value 屬性對(duì)應(yīng)的整數(shù)放到解析上下文中的棧頂堰怨。在這個(gè)例子中芥玉,也就是 calculator2.xml,這個(gè)值是 7 與 3备图。為了響應(yīng) <add> 元素灿巧。AddAction
實(shí)例將會(huì)獲取之前放進(jìn)去的兩個(gè)整數(shù),計(jì)算它們的和揽涮,然后再放進(jìn)去砸烦。如,在解析上下文棧頂?shù)木褪?10 (= 7 + 3)绞吁。下個(gè) literal 元素將會(huì)讓 LiteralAction 將會(huì)將整數(shù) 3 放入棧頂幢痘。為了響應(yīng) <multiply> 元素,MultiplyAction
將會(huì)獲取之前放入的兩個(gè)整數(shù) 10 與 3家破,然后計(jì)算它們的乘積颜说。它會(huì)將結(jié)果 30 放入棧頂。最后汰聋,為了響應(yīng)結(jié)束事件對(duì)應(yīng)的 </computation> 標(biāo)簽门粪,ComputationAction1 將會(huì)打印堆棧頂部的結(jié)果,因此烹困,運(yùn)行:
java chapters.onJoran.calculator.Calculator1 src/main/java/chapters/onJoran/calculator/calculator2.xml
將會(huì)輸出:
The computation named [toto] resulted in the value 30
默認(rèn)動(dòng)作
目前定義的隊(duì)則都是被顯示的動(dòng)作調(diào)用玄妈。因?yàn)楫?dāng)前元素相關(guān)的模式/動(dòng)作能夠在規(guī)則存儲(chǔ)中被找到。但是,在高度可以擴(kuò)展的系統(tǒng)中拟蜻,組件的數(shù)量跟類(lèi)型都會(huì)非常多绎签,因此對(duì)所有的模式都關(guān)聯(lián)一個(gè)具體的動(dòng)作將會(huì)變得十分的蛋疼。
同時(shí)酝锅,甚至在高擴(kuò)展的系統(tǒng)中诡必,可以看到重復(fù)的規(guī)則關(guān)聯(lián)了不同的部分。如果我們可以識(shí)別這些規(guī)則搔扁,那么我們就可以在編譯時(shí)處理由子組件組成的未知組件爸舒。例如,Apache Ant 有能力在編譯時(shí)處理包含未知標(biāo)簽的任務(wù)稿蹲,僅僅通過(guò)檢查組件中的方法名是不是以 add 開(kāi)頭扭勉,像 addFile
或者 addClassPath
之類(lèi)的。當(dāng) Ant 在任務(wù)內(nèi)遇到一個(gè)內(nèi)置的標(biāo)簽苛聘,它僅僅實(shí)例化一個(gè)匹配了任務(wù)類(lèi) add 方法的簽名的對(duì)象涂炎,并且將結(jié)果對(duì)象附加到父級(jí)上。
Joran 通過(guò)默認(rèn)動(dòng)作的形式來(lái)提供類(lèi)似的功能焰盗。Joran 保留了一系列的默認(rèn)動(dòng)作璧尸,如果當(dāng)前模式?jīng)]有具體的模式可以匹配時(shí),它們將會(huì)被應(yīng)用熬拒。但是爷光,應(yīng)用默認(rèn)的動(dòng)作可能并不總是合適的。在執(zhí)行默認(rèn)的動(dòng)作之前澎粟,Joran 會(huì)詢問(wèn)指定的動(dòng)作當(dāng)前的情況是否合適蛀序。只有動(dòng)作返回肯定的回答,Joran 的配置器才會(huì)調(diào)用默認(rèn)的動(dòng)作活烙。注意徐裸,這個(gè)額外的步驟可能支持多個(gè)默認(rèn)的動(dòng)作,如果在給定的情況下啸盏,沒(méi)有合適的默認(rèn)動(dòng)作重贺,也可能一個(gè)都不支持。
你可以創(chuàng)建并注冊(cè)一個(gè)自定義的默認(rèn)動(dòng)作回懦。見(jiàn)下一個(gè)示例气笙。該示例位于 logback-examples/src/main/java/chapters/onJoran/implicit 文件夾下。
PrintMe
應(yīng)用將一個(gè) NOPAction
實(shí)例與 "*/foo" 模式相關(guān)聯(lián)怯晕,也就是與名字叫做 "foo" 的任何元素潜圃。正如它的名字所示, NOPAction
的 begin()
與 end()
方法都為空舟茶。PrintMe
應(yīng)用仍然會(huì)在它的默認(rèn)動(dòng)作列表注冊(cè)一個(gè) PrintMeImplicitAction 的實(shí)例谭期。PrintMeImplicitAction
對(duì)任何 printme 屬性為 true 的元素有效堵第。參見(jiàn) PrintMeImplicitAction
的 isApplicable()
方法。PrintMeImplicitAction
的 begin()
方法會(huì)在控制臺(tái)打印當(dāng)前元素的名字隧出。
implicit1.xml XML 文檔說(shuō)明了默認(rèn)動(dòng)作是如何起作用的踏志。
Example: implicit1.xml
<foo>
<xyz printme="true">
<abc printme="true"/>
</xyz>
<xyz/>
<foo printme="true"/>
</foo>
運(yùn)行:
java chapters.onJoran.implicit.PrintMe src/main/java/chapters/onJoran/implicit/implicit1.xml
輸出:
Element [xyz] asked to be printed.
Element [abc] asked to be printed.
20:33:43,750 |-ERROR in c.q.l.c.joran.spi.Interpreter@10:9 - no applicable action for [xyz], current pattern is [[foo][xyz]]
給定一個(gè) NOPAction
實(shí)例與 "/foo" 實(shí)例相關(guān)聯(lián),NOPAction
的 begin()
與 end()
方法在 <foo> 元素上被調(diào)用鸳劳。PrintMeImplicitAction
不會(huì)在任何 <foo> 元素上觸發(fā)狰贯。對(duì)于其它的元素也搓,因?yàn)闆](méi)有明確的動(dòng)作可以匹配赏廓,所以 PrintMeImplicitAction
的 isApplicable()
方法被調(diào)用。它只有在 printme 屬性設(shè)置為 true 的時(shí)候才會(huì)返回 true傍妒。也就是第一個(gè) <xyz> 元素 (不是第二個(gè)) 與 <abc> 元素幔摸。第十行的第二個(gè) <xyz> 元素,沒(méi)有可用的動(dòng)作颤练,所以生成了一個(gè)內(nèi)部的錯(cuò)誤信息既忆。這個(gè)信息通過(guò) PrintMe
的最后一行代碼 StatusPrinter.print
來(lái)進(jìn)行輸出。這也解釋了上面的輸出嗦玖。
在實(shí)踐中使用默認(rèn)動(dòng)作
logback-classic 與 logback-access 各自的 Joran 配置器只包含兩個(gè)默認(rèn)的動(dòng)作患雇,叫做 NestedBasicPropertyIA
與 NestedComplexPropertyIA
。
NestedBasicPropertyIA
適用于任何屬性的類(lèi)型為原始類(lèi)型 (或者 equivalent object type in the java.lang
包中的對(duì)象類(lèi)型 )宇挫,枚舉類(lèi)苛吱,或者其它遵循 "valuesOf" 約定的類(lèi)型。這些屬性被稱之為基本 或者簡(jiǎn)單 屬性器瘪。如果一個(gè)類(lèi)它包含一個(gè)名為 valueOf()
的靜態(tài)方法翠储,接受一個(gè) java.lang.String
作為參數(shù)并且返回相關(guān)類(lèi)型的實(shí)例,那么就說(shuō)這個(gè)類(lèi)遵循 "valueOf" 約定橡疼。目前援所,Level
,Duration
欣除,以及 FileSize
類(lèi)遵循這個(gè)約定住拭。
NestedComplexPropertyIA
適用于 NestedBasicPropertyIA
不適用的情況,并且如果對(duì)象棧頂部的對(duì)象具有當(dāng)前屬性名的 set 與 add 方法历帚,那么該屬性名相當(dāng)于當(dāng)前元素的名稱滔岳。這些屬性可以反過(guò)來(lái)包含其它的組件。因此抹缕,這些屬性可以說(shuō)是有點(diǎn)復(fù)雜澈蟆。由于復(fù)雜屬性的存在,NestedComplexPropertyIA
將會(huì)為內(nèi)部組件實(shí)例化一個(gè)合適的類(lèi)卓研,并且通過(guò)使用父組件以及內(nèi)部元素名的 set/add 方法將其附加到父組件上 (在對(duì)象棧的頂部)趴俘。相應(yīng)的類(lèi)通過(guò)當(dāng)前 (內(nèi)置) 元素的 class 屬性來(lái)指定睹簇。但是,如果沒(méi)有指定 class 屬性寥闪,那么將會(huì)根據(jù)是否滿足以下其中之一的條件來(lái)使用默認(rèn)的類(lèi)名太惠。
- 父對(duì)象屬性指定的類(lèi)有內(nèi)部的規(guī)則相關(guān)聯(lián)
- set 方法包含一個(gè)指定類(lèi)的 @DefaultClass 屬性
- set 方法的參數(shù)類(lèi)型是一個(gè)含有共有構(gòu)造方法的實(shí)體類(lèi)
默認(rèn)類(lèi)映射
在 logback-classic,有一些內(nèi)部的規(guī)則將父 類(lèi)/屬性 名映射為默認(rèn)的類(lèi)疲憋。這些規(guī)則如下表所示:
父類(lèi) | 屬性名 | 默認(rèn)類(lèi) |
---|---|---|
ch.qos.logback.core.AppenderBase | encoder | ch.qos.logback.classic.encoder.PatternLayoutEncoder |
ch.qos.logback.core.UnsynchronizedAppenderBase | encoder | ch.qos.logback.classic.encoder.PatternLayoutEncoder |
ch.qos.logback.core.AppenderBase | layout | ch.qos.logback.classic.PatternLayout |
ch.qos.logback.core.UnsynchronizedAppenderBase | layout | ch.qos.logback.classic.PatternLayout |
ch.qos.logback.core.filter.EvaluatorFilter | evaluator | ch.qos.logback.classic.boolex.JaninoEventEvaluator |
在以后的版本中凿渊,這個(gè)列表可能會(huì)發(fā)生改變。最新的規(guī)則缚柳,請(qǐng)查看 logback-classic 中 JoranConfigurator 中的 addDefaultNestedComponentRegistryRules
方法埃脏。
屬性集
除了單個(gè)簡(jiǎn)單屬性以及單個(gè)復(fù)雜屬性外,logback 的默認(rèn)動(dòng)作支持屬性集秋忙,它們可以是簡(jiǎn)單的或者復(fù)雜的彩掐。指定的屬性通過(guò) "add" 方法,而不是 set 方法苦蒿。
動(dòng)態(tài)添加新規(guī)則
Joran 包含一個(gè)允許 Joran 在解析 XML 文檔的過(guò)程中,動(dòng)態(tài)解析新規(guī)則的動(dòng)作。示例代碼,查看 logback-examples/src/main/java/chapters/onJoran/newRule/ 文件夾夯膀。在這個(gè)包中俺猿,NewRuleCalculator
僅僅設(shè)置兩個(gè)規(guī)則谊惭,一個(gè)用來(lái)處理最頂層的元素,一個(gè)用來(lái)學(xué)習(xí)新規(guī)則癌佩。下面是 NewRuleCalculator
中相關(guān)的代碼:
ruleMap.put(new Pattern("*/computation"), new ComputationAction1());
ruleStore.addRule(new Pattern("/computation/newRule"), new NewRuleAction());
NewRuleAction
是 logback-core 的一部分放案,跟其它的動(dòng)作非常的類(lèi)似。它有 begin()
與 end()
方法稿湿,在每次解析器找到一個(gè) newRule 元素時(shí)被調(diào)用。當(dāng)被調(diào)用時(shí)再姑,begin()
方法會(huì)去尋找 pattern 與 actionClass 屬性。然后實(shí)例化相應(yīng)的動(dòng)作類(lèi)找御,并將模式/動(dòng)作作為一條新規(guī)則添加到 Joran 的規(guī)則存儲(chǔ)中元镀。
下面是如何在 xml 文件添加一條新規(guī)則:
<newRule pattern="*/computation/literal"
actionClass="chapters.onJoran.calculator.LiteralAction"/>
使用 newRule 聲明谜嫉,我們可以看到 NewRuleCalculator
表現(xiàn)出跟之前看到的 Calculator1
同樣的結(jié)果。包括計(jì)算在內(nèi)凹联,我們可以按照如下的方式進(jìn)行表示:
Example: newRule.xml
<computation name="toto">
<newRule pattern="*/computation/literal"
actionClass="chapters.onJoran.calculator.LiteralAction"/>
<newRule pattern="*/computation/add"
actionClass="chapters.onJoran.calculator.AddAction"/>
<newRule pattern="*/computation/multiply"
actionClass="chapters.onJoran.calculator.MultiplyAction"/>
<computation>
<literal value="7"/>
<literal value="3"/>
<add/>
</computation>
<literal value="3"/>
<multiply/>
</computation>
java chapters.onJoran.newRule.NewRuleCalculator src/main/java/chapters/onJoran/newRule/newRule.xml
作者原文中的命令寫(xiě)了兩個(gè) java
輸出:
The computation named [toto] resulted in the value 30
跟之前的結(jié)果一致沐兰。
運(yùn)行原文中的示例并不會(huì)輸出坐著所說(shuō)的結(jié)果,需要去掉 <computation> 中的 <computation> 標(biāo)簽才可以