Tomcat 配置文件解析工具 Digester

Digester 是一個依據 xml 配置文件動態構建 Java 對象樹的工具,基於 SAX 解析器進行封裝,它爲 SAX 事件的處理提供了更高級和友好的接口,讓開發更專注於要執行的處理,隱藏了 XML 元素詳細的層次結構信息。

1. 對象堆棧

爲了便於實現,內部使用堆棧存儲創建的對象。當滿足元素匹配模式時,按預設的處理規則操作棧中對象。

典型的創建對象處理邏輯是,觸發創建新對象的規則,在遇到特定 XML 元素的開頭時將其推送到棧頂;處理此元素的嵌套內容和屬性時,該對象將保留在堆中;當遇到元素的末尾時,將它彈出。

Digester 提供的處理規則解決了這種設計的幾個潛在問題:

  • 如何將正在創建的對象相互關聯?SetNextRule 規則將棧頂對象作爲參數傳遞給堆棧下一個頂層對象的命名方法(反之亦然),此規則可以輕鬆地在這些對象之間建立父子關係,一對一和一對多的關係也很容易構建。
  • 如何保留對第一個創建對象的引用?當遇到開始元素的結束標籤時,所有對象從棧中彈出,但 parse() 方法會返回第一個創建的對象,也可以在解析前將某個應用程序對象的引用推送到堆棧,並手動設置和動態創建對象之間的父子關係。

2. 元素模式匹配

Digester 解析器的一個主要特性是自動確定正在解析的 XML 文檔的元素層次結構,開發人員只需在解析過程中遇到某種嵌套元素排列時,決定要執行哪些函數。元素匹配模式,確認的就是執行函數也就是規則。

一個非常簡單的元素匹配模式是一個簡單的字符串,如"a"。只要在 XML文檔中遇到頂級元素 <a>,無論它發生多少次,都會匹配此模式。請注意,嵌套的 <a> 元素將不匹配此模式。

比較複雜的是匹配嵌套元素,如 "a/b",當找到嵌套在頂級 <a> 元素內的 <b> 元素時,將匹配此模式。同樣,這種匹配可以根據需要多次發生,具體取決於要解析的XML文檔的內容。也可以使用多個斜槓來定義要匹配的任何所需深度的層次結構。

例如,假設已註冊與 "a", "a/b", 和 "a/b/c" 模式匹配的處理規則。對於具有以下內容的輸入XML文檔,在解析相應元素時將匹配指示的模式:

<a>         -- 匹配 "a"
  <b>       -- 匹配 "a/b"
    <c/>    -- 匹配 "a/b/c"
    <c/>    -- 匹配 "a/b/c"
  </b>
  <b>       -- 匹配 "a/b"
    <c/>    -- 匹配 "a/b/c"
    <c/>    -- 匹配 "a/b/c"
    <c/>    -- 匹配 "a/b/c"
  </b>
</a>

通過在匹配的模式字符串中使用 "*" 通配符,也可以匹配特定的XML元素,無論它是如何嵌套(或不嵌套)在XML文檔中。例如,元素匹配模式 "*/a" 將匹配文檔內任何嵌套位置的元素 <a>。

如果一個匹配模式對應多個處理規則,將按順序觸發。 begin(和body)方法按照最初向 Digester 註冊的 Rules 的順序執行,而 end 方法調用以相反的順序執行。 換句話說 - 順序是先進先出的。

3. 處理規則

處理規則定義的時模式匹配時應該發生的動作,它通常時 Rule 接口的子類,每個規則實現了以下一個或多個事件方法,這些方法會在解析的過程中執行:

  • begin() - 遇到匹配的 XML 元素的開頭時調用,並傳遞包含與該元素對應的所有屬性的數據結構
  • body() - 遇到匹配元素的嵌套內容(不是 XML 元素,文本內容)時調用,在解析過程中,任何開始或結束的空格都被刪除
  • end() - 遇到匹配的 XML 元素的結尾時調用,如果此元素包含嵌套的 XML 元素,則在調用此方法之前,已完成嵌套元素匹配的規則
  • finish() - 在 XML 文檔結束,完成解析時調用,爲每個規則提供清理他們可能已創建和緩存的任何臨時數據的機會

Digester 提供了一組處理規則實現類,用於處理許多常見的編程場景,這些類分別是:

  • ObjectCreateRule - begin()方法被調用時,此規則將實例化指定 Java 類的新實例,並將其推送到堆棧。默認使用的類名是根據傳遞給構造函數的參數,但也可以通過正在處理的 XML 元素指定屬性及類名被覆蓋。當 end()方法被調用時,棧頂的對象將被彈出,並且 Digester 內它的任何引用將被丟棄。
  • FactoryCreateRule - ObjectCreateRule 的一種變體,當要創建對象實例的 Java 類沒有無參數構造函數,或者希望在將對象移交給 Digester 之前執行其他處理時,它很有用。
  • SetPropertiesRule - begin()方法被調用時,Digester 使用 Java Reflection API 獲取棧頂對象屬性的 setter 方法名稱,並與 XML 元素上指定的屬性相匹配,然後執行設置相應的屬性值。一個常見用法是:定義一個 ObjectCreateRule,然後是一個 SetPropertiesRule,它們具有相同的元素匹配模式。
  • SetPropertyRule - begin()方法被調用時,Digester 調用棧頂對象指定屬性的 setter 方法,屬性的 key-value 由 XML 設置。當 XML 文件符合特定的 DTD,並且希望配置 DTD 中沒有相應屬性的特定屬性時,這非常有用。
  • SetNextRule - end()方法被調用時,Digester 分析棧中的下一個棧頂元素,查找指定屬性的 setter 方法,然後它調用此方法,將棧頂對象作爲參數傳遞。此規則通常用於在兩個對象之間建立一對多關係,方法名稱通常類似於 "addChild"。
  • CallMethodRule - 在 end()方法被調用時執行,設置棧頂對象特定方法的調用,可指定方法名稱和參數,實際的參數通常在觸發此規則元素的 body 中。
  • CallParamRule - 此規則標識我們嵌套的 CallMethodRule 參數的來源,可以指定參數值取自特定的命名屬性,或者取自此元素嵌套的 body 內容。

4. 用法示例 - 創建簡單的對象樹

假設有兩個簡單的 JavaBeans,Foo 和 Bar,方法簽名如下:

package mypackage;
public class Foo {
  public void addBar(Bar bar);
  public Bar findBar(int id);
  public Iterator getBars();
  public String getName();
  public void setName(String name);
}

public mypackage;
public class Bar {
  public int getId();
  public void setId(int id);
  public String getTitle();
  public void setTitle(String title);
}

並且使用 Digester 來解析以下 XML 文檔:

<foo name="The Parent">
  <bar id="123" title="The First Child"/>
  <bar id="456" title="The Second Child"/>
</foo>

一個簡單的方法是使用 Digester 設置如下方式的解析規則,然後處理包含此文檔的輸入文件:

Digester digester = new Digester();
digester.setValidating(false);
digester.addObjectCreate("foo", "mypackage.Foo");
digester.addSetProperties("foo");
digester.addObjectCreate("foo/bar", "mypackage.Bar");
digester.addSetProperties("foo/bar");
digester.addSetNext("foo/bar", "addBar", "mypackage.Bar");
Foo foo = (Foo) digester.parse();

這些規則將按順序執行以下任務:

  1. 遇到最外層的 <foo> 元素時,創建 mypackage.Foo 的新實例並將其推送到對象堆棧;在 <foo> 元素的末尾,此對象將從堆棧中彈出。
  2. 基於此 XML 元素的屬性值設置剛剛創建和推送到棧頂的對象,Foo 的屬性。
  3. 遇到嵌套的 <bar> 元素時,創建 mypackage.Bar 的新實例並將其推送到對象堆棧;在<bar>元素的末尾,該對象將從堆棧中彈出(即在處理匹配 foo/bar 的其餘規則之後)。
  4. 基於此 XML 元素的屬性值設置剛剛創建和推送到棧頂的對象,Bar 的屬性。注意 id,屬性的 String -> int,要執行類型轉換
  5. 調用 next-to-top,即下一個棧頂元素上的 addBar 方法(這就是爲什麼被稱爲 "set next" 規則),並將當前棧頂元素作爲參數,從而建立父/子關係。

解析完成後,第一個被推入堆棧的對象(本例中爲 Foo 對象)將返回,它的屬性已被初始化,幷包含創建的所有子 Bar 對象。

5. 小結

每個框架都有自己解析 XML 的方法,Tomcat 也不例外,本文對 Digester 的基本原理進行了總結,使用還是比較簡單方便的。

此外,Digester 還有一些其他設置,比如指定創建對象使用的類加載器,是否以命名空間的方式解析,是否根據指定的 DTD 驗證文檔,還有 RuleSet 可重用規則集的使用等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章