Tomcat組件梳理—Digester的使用


Tomcat組件梳理—Digester的使用

再吐槽一下,本來以為可以不用再開一個篇章來梳理Digester了,但是發現在研究Service的創建時,還是對Digester的很多接口或者機制不熟悉,簡直搞不懂。想想還是算了,再回頭一下,把這個也給梳理了。所以此文主要做兩件事情,

1.梳理Digester的設計思想和常用接口。

2.梳理Digester對server.xml文件的解析。這么看也算是理論和實際相結合吧。

1.XML文件解析的兩種方案。

Java解析XML文件有兩個主要的思想,分別是:

1.1.預加載DOM樹

該方法的思路是:將整個XML文件讀取到內存中,在內容中構造一個DOM樹,也叫對象模型集合,然后java代碼只需要操作這個樹就可以。

該方法的主要實現為DOM解析,在此基礎上有兩個擴展:JDOM解析,DOM4J解析。這兩種方法的思路都是一樣的,只不過一個是官方出的,一個是社區出的,好不好用的問題。Java很奇怪,都是社區出的更好用,即DOM4J。

該思想有優點和缺點:

  • 優點:使用DOM時,XML文檔的結構在內存中很清晰,元素與元素之間的關系保留了下來。即能記錄文檔的所有信息。
  • 缺點:如果XML文檔非常大,把整個XML文檔裝在進內存容易造成內容溢出,無法加載了。

1.2.事件機制的SAX

該方法的思路是:一行一行的讀取XML文件,沒遇到一個節點,就看看有沒有監聽該事件的監聽器,如果有就觸發。當XML文件讀取結束后,內存里不會保存任何數據,整個XML的解析就結束了。所以,這里面最重要的是對感興趣的節點進行監聽和處理。

該思想仍然有優點和缺點:

  • 優點:使用SAX不會占用大量內存來保存XML文檔數據,效率高。
  • 缺點:當解析一個元素時,上一個元素的信息已經丟棄,也就是說沒有保存元素與元素之間的結構關系。

2.Digester的設計思想

2.1.主要解決的問題

Digester是對SAX的封裝和抽象,解決在SAX中特別不好用的地方,那么Digester主要解決了哪些問題呢?

  • 1.XML的內容是變化的,那么不同的內容如何采用不同的方法去處理呢?用多個if進行判斷?顯然不是。

    Digester解決該問題的方法是,抽象出兩個東西,一個是XML里面的內容,即XML中的節點,該內容可以使用正則進行書寫。另一個是方法,找到該節點時需要調用的處理方法。處理方法統一繼承Rule接口。

    這樣當在讀取XML文件時,發現新的節點,就可以去找對應的正則,如果匹配,就可以調用對應的處理方法了。

  • 2.解析一個XML上的節點時,該如何解析?

    Digester解決該方法時,是將XML節點解析的生命周期進行抽象,不同生成周期做不同的事情,不同的處理方法只需要重寫生命周期對應的方法就行。

  • 3.如何確保XML上不同的節點有依賴關系呢?

    Digester在內部自己維護了一個棧的結構,每遇到一個新的節點時,把這個對象把它壓入棧(push),節點解析結束時,可以再從棧中彈出(pop),棧里面最頂部的對象永遠都是現在正在解析的對象,第二個是它的父節點,提供API調用父節點的方法把引用當前對象,這樣就解決了依賴的問題。

2.2.源碼主要結構

先對架構進行解釋一下,

  • 最上面的DefaultHandler來自SAX,Digester集成該類,足以說明Digester底層用的是SAX了。

  • Client表示調用Digester的Java代碼。

  • Digester是真個Digester組件的核心類和入口類,Client需要實例化該類,設置改類,並調用里面的參數。

  • Rules是一個保存XML的節點和規則的映射關系的接口,默認實現類是RulesBase

  • RuesBaseRules的默認實現類。當有一個XML節點開始解析時,會在這里面找是否有對應的節點,並根據節點查找對應的處理規則。

  • Rule對節點的處理方法的接口,內置的或者自定義的規則都是集成該接口。

  • **Rule是接口Rule的很多實現,根據具體的實現不同而不同。

使用Digester解析XML文檔的流程:

  • 1.首先,Client需要創建一個Digester對象。
  • 2.然后,Client必須根據自己的XML格式來添加所有的Rule。
  • 3.添加完Rule后,Client調用Digester的parse操作來解析XML文件。
  • 4.Digester實現了SAX的接口,解析時遇到具體的XML對象時會調用startElement等接口函數。
  • 5.在這些SAX接口函數中,會掃描規則鏈(RulesBase),找到匹配規則,規則匹配一般都是根據具體的元素名稱來進行匹配。
  • 6.找到對應的rule后,依次執行rule,這里的startElement對應的是begin操作,endElement對應的是end操作,具體要做的事情都在每個rule的begin,body,end函數中。
  • 7.文檔結束后,會執行所有rule的finish函數。

Digester的巧妙設計:

1.XML格式變化:Digester把這個變化拋給具體用戶去解決,用戶在使用Digester之前必須自己根據自己的XML文件格式來構造規則鏈,Digester只提供構造規則鏈的手段,體現了有所為有所不為的設計思想。

2.處理方式變化的問題,Digester將處理方式抽象為規則Rule,一個規則對應一個處理方式,Digester提供了通用的缺省的Rule,如果覺得提供的規則不滿足自己的要求,可以自己另外定制。

Digester在代碼中的使用流程

    //3.用digester解析server.xml文件,把配置文件中的配置解析成java對象
    //3.1.准備好用來解析server.xml文件需要用的digester。
    Digester digester = createStartDigester();
    //3.2.server.xml文件作為一個輸入流傳入
    File file = configFile();
    InputStream inputStream = new FileInputStream(file);
    //3.3.使用inputStream構造一個sax的inputSource
    InputSource inputSource = new InputSource(file.toURI().toURL().toString());
    inputSource.setByteStream(inputStream);
    //3.4.把當前類壓入到digester的棧頂,用來作為digester解析出來的對象的一種引用
    digester.push(this);
    //3.5.調用digester的parse()方法進行解析。
    digester.parse(inputSource);
  • 3.1.准備好用來解析server.xml文件需要用的digester。
  • 3.2.讀取server.xml文件作為一個輸入流。
  • 3.3.使用inputStream構造一個sax的inputSource,因為digester底層用的是sax去解析的。
  • 3.4.把當前類壓入到digester的棧頂,用來作為digester解析出來的對象的一種引用,digester自帶一個棧的結構。
  • 3.5.調用digester的parse()方法進行解析。前面幾步都是在准備環境,這里才是正真的去解析了。

2.3.內部維護的棧結構

Digester用棧來維護每個正在解析的對象,注意,這個棧里面存放的是正在解析的,即開始解析時就放入棧,解析玩就彈出棧。

這個棧是Digester自己寫的一個,用ArrayList來實現的,但是方法都一樣,在Digester類中進行維護,代碼為:

//org.apache.tomcat.util.digester.Digester
/**
 * The object stack being constructed.
 */
protected ArrayStack<Object> stack = new ArrayStack<>();

該棧的主要方法有:

  • clear():清楚棧內的所有元素。
  • peek():返回棧頂對象的引用,而不進行刪除。
  • pop():彈出棧頂的元素。
  • push():壓入一個元素到棧頂。

3.Digester的常用接口

3.1.解析XML節點時的生命周期

在解析XML節點時,有一整個生命周期在里面,解析一個節點時,會有一下生命周期,1.遇到匹配的節點的開始部分時開始解析,2.解析文本內容,3.遇到匹配的節點的結束部分時結束解析,4.最后處理資源。

  • begin():當讀取到匹配節點的開始部分時調用,會將該節點的所有屬性作為從參數傳入。

  • body():當讀取到匹配節點的內容時調用,注意指的不是子節點,而是嵌入內容為普通文本。

  • end():當讀取到匹配節點的結束部分時調用,如果存在子節點,只有當子節點處理完畢后該方法才會被調用。

  • finish():當整個parse()方法完成時調用,多用於清楚臨時數據和緩存數據。

3.2.內置的規則接口

Digester內置了一些規則,可以調用接口直接使用,還有一些其他的API可調用:

  • ObjectCreateRule: 當begin()方法調用時,該規則會將指定的Java類實例化,並將其放入對象棧。具體的Java類可由該規則在構造方法出啊如,也可以通過當前處理XML節點的某個屬性指定,屬性名稱通過構造方法傳入。當end()方法調用時,該規則創建的對象將從棧中取出。
  • FactoryCreateRule:ObJectCreateRule規則的一個變體,用於處理Java類無默認構造方法的情況,或者需要在Digester處理該對象之前執行某些操作的情況。
  • SetPropertiesRule:當begin()方法調用時,Digester使用標准的Java Bean屬性操作方法(setter)將當前XML節點的屬性值設置到對象棧頂部的對象中。
  • SetPropertyRule:當begin()方法調用時,Digester會設置棧頂部對象指定屬性的值,其中屬性名和屬性值分別通過XML節點的兩個屬性指定。
  • SetNextRule:當end()方法調用時,Digester會找到位於棧頂部對象的下一個對象,並調用其指定的方法,同時將棧頂部對象作為參數傳入,用於設置父對象的子對象,以便在棧對象之間建立父子關系,從而形成對象樹,方便引用。
  • SetTopRule:與setNextRule對象,當end()方法調用時,Digester會找到位於站頂部的對象,調用其指定方法,同時將位於頂部下一個對象作為參數傳入,用於設置當前對象的父對象。
  • CallMethRule:該規則用於在end()方法調用時執行棧頂對象的某個方法,參數值由CallParamRule獲取。
  • CallParamRule:該規則與CallMethodRule配合使用,作為其子節點的處理規則創建方法參數,參數值可取自某個特殊屬性,也可以取自節點的內容。
  • NodeCreateRule:用於將XML文檔樹的一部分轉換為DOM節點,並放入棧。

3.3.自定義規則

如果需要自定義規則,只需要創建一個類繼承Rule接口,並重寫里面的生命周期方法,可以選擇只重寫部分。

例如:

public class ConnectorCreateRule extends Rule {
 
  @Override
    public void begin(String namespace, String name, Attributes attributes){
      //do something
    }
  
  @Override
    public void begin(String namespace, String name, Attributes attributes){
      //do something
    }
}

最后在使用自定義的規則時,方法如下:

digester.addRule("Server/Service/Connector",new ConnectorCreateRule());

此時即可。

3.4.addRuleSet方法

很多時候針對同一個節點,我們會執行很多規則,比如:創建一個對象,設置屬性,調用方法。至少三個。每次都寫三個是比較麻煩的,那么有沒有比較方便的方法呢?

有。使用addRuleSet()方法,傳入需要匹配的節點名稱,然后對該節點使用多個規則。此方法方便重用。

案例:

首先自定義一個規則:

public class MyRuleSet
  extends RuleSetBase {

  public MyRuleSet()
  {
    this("");
  }

  public MyRuleSet(String prefix)
  {
    super();
    this.prefix = prefix;
    this.namespaceURI = "http://www.mycompany.com/MyNamespace";
  }

  protected String prefix = null;

  public void addRuleInstances(Digester digester)
  {
    digester.addObjectCreate( prefix + "foo/bar",
      "com.mycompany.MyFoo" );
    digester.addSetProperties( prefix + "foo/bar" );
  }

}

然后可以直接調用:

  Digester digester = new Digester();
  ... configure Digester properties ...
  digester.addRuleSet( new MyRuleSet( "baz/" ) );

4.Digester解析Server.xml的案例分析

上面說了這么多,終於可以開始梳理Tomcat中Digester對server.xml文件的解析了。

4.1.總的步驟

使用Digester的方法在上面有說過,再展示一下:

    //3.用digester解析server.xml文件,把配置文件中的配置解析成java對象
    //3.1.准備好用來解析server.xml文件需要用的digester。
    Digester digester = createStartDigester();
    //3.2.server.xml文件作為一個輸入流傳入
    File file = configFile();
    InputStream inputStream = new FileInputStream(file);
    //3.3.使用inputStream構造一個sax的inputSource
    InputSource inputSource = new InputSource(file.toURI().toURL().toString());
    inputSource.setByteStream(inputStream);
    //3.4.把當前類壓入到digester的棧頂,用來作為digester解析出來的對象的一種引用
    digester.push(this);
    //3.5.調用digester的parse()方法進行解析。
    digester.parse(inputSource);

解釋一下:

  • 3.1.准備好用來解析server.xml文件需要用的digester。
  • 3.2.讀取server.xml文件作為一個輸入流。
  • 3.3.使用inputStream構造一個sax的inputSource,因為digester底層用的是sax去解析的。
  • 3.4.把當前類壓入到digester的棧頂,用來作為digester解析出來的對象的一種引用,digester自帶一個棧的結構。
  • 3.5.調用digester的parse()方法進行解析。前面幾步都是在准備環境,這里才是正真的去解析了。

在第1個步驟中,需要先構造一個套規則,存放在digester實例中。構造代碼比較多,我拆開說

4.2.節點綁定規則

以下均為createStartDigester()方法的內容

4.2.1.設置giester的參數:

// 創建一個digester實例
Digester digester = new Digester();
//是否對xml文檔進行類似XSD等類型的校驗,默認為fasle。
digester.setValidating(false);
//是否進行節點設置規則校驗,如果xml中相應節點沒有設置解析規則會在控制台顯示提示信息。
digester.setRulesValidation(true);

//將XML節點中的className作為假屬性,不必調用默認的setter方法
//(一般的節點屬性在解析時會以屬性作為入參調用該節點相應對象的setter方法,而className屬性的作用
// 提示解析器使用該屬性的值作來實例化對象)
Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
List<String> objectAttrs = new ArrayList<>();
objectAttrs.add("className");
fakeAttributes.put(Object.class, objectAttrs);
// Ignore attribute added by Eclipse for its internal tracking
List<String> contextAttrs = new ArrayList<>();
contextAttrs.add("source");
fakeAttributes.put(StandardContext.class, contextAttrs);
digester.setFakeAttributes(fakeAttributes);

//使用當前線程的上下文類加載器,主要加載FactoryCreateRule和ObjectCreateRule
digester.setUseContextClassLoader(true);

4.2.2.Server的解析

1.創建Server實例

digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");
digester.addSetProperties("Server");
digester.addSetNext("Server","setServer","org.apache.catalina.Server");

創建對象,設置屬性,調用方法添加到Catalina類中。

2.為Server添加全局J2EE企業命名上下文

digester.addObjectCreate("Server/GlobalNamingResources",
                         "org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources", 
                    "setGlobalNamingResources",
                    "org.apache.catalina.deploy.NamingResourcesImpl");

創建對象,設置屬性,添加到Server中。

3.為Server添加生命周期監聽器

digester.addObjectCreate("Server/Listener",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
                    "addLifecycleListener",
                    "org.apache.catalina.LifecycleListener");

給Server添加監聽器,Server一共添加了5個監聽器,xml文件中顯示如下:

  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

這5個監聽器的作用分別是:

  • VersionLoggerListener:在Server初始化之前打印操作系統,JVM以及服務器的版本信息。
  • AprLifecycleListener:在Server初始化之前加載APR庫,並於Server停止之后銷毀。
  • JreMemoryLeakPreventionListener:在Server初始化之前調用,以解決單例對象創建導致的JVM內存泄漏問題以及鎖文件問題。
  • GlobalResourcesLifecycleListener:在Server啟動時,將JNDI資源注冊為MBean進行管理。
  • ThreadLocalLeakPreventionListener:用於在Context停止時重建Exceutor池中的線程,避免導致內存泄漏。

4.給Server添加Service實例

digester.addObjectCreate("Server/Service",
                         "org.apache.catalina.core.StandardService",
                         "className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
                    "addService",
                    "org.apache.catalina.Service");

創建Service對象,設置參數,添加到Server中。

5.給Service添加生命周期監聽器

digester.addObjectCreate("Server/Service/Listener",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener",
                    "addLifecycleListener",
                    "org.apache.catalina.LifecycleListener");

具體的監聽器由className屬性指定,默認情況下,Catalina未指定Service監聽器。

6.為Service添加Executor

digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
digester.addSetProperties("Server/Service/Executor");
digester.addSetNext("Server/Service/Executor",
                    "addExecutor",
                    "org.apache.catalina.Executor");

通過該配置我們可以知道,Catalina共享Excetor的級別為Service,Catalina默認情況下未配置Executor,即不共享。

7.為Service添加Connector

digester.addRule("Server/Service/Connector",
                 new ConnectorCreateRule());

digester.addRule("Server/Service/Connector",
                 new SetAllPropertiesRule(new String[]{"executor", 		       "sslImplementationName"}));
digester.addSetNext("Server/Service/Connector",
                    "addConnector",
                    "org.apache.catalina.connector.Connector");

設置相關屬性時,將executor和sslImplementationName屬性排除,因為在Connector創建時,會判斷當前是否指定了executor屬性,如果是,則從Service中查找該名稱的executor並設置到Connector中。同樣,Connector創建時,也會判斷是否添加了sslIlplementationName屬性,如果是,則將屬性值設置到使用的協議中,為其指定一個SSL實現。

8.Connector添加虛擬主機SSL配置

digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
                         "org.apache.tomcat.util.net.SSLHostConfig");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
digester.addSetNext("Server/Service/Connector/SSLHostConfig",
                    "addSslHostConfig",
                    "org.apache.tomcat.util.net.SSLHostConfig");

digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                 new CertificateCreateRule());
digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                 new SetAllPropertiesRule(new String[]{"type"}));
digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
                    "addCertificate",
                    "org.apache.tomcat.util.net.SSLHostConfigCertificate");

digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                         "org.apache.tomcat.util.net.openssl.OpenSSLConf");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                    "setOpenSslConf",
                    "org.apache.tomcat.util.net.openssl.OpenSSLConf");

digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                         "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                    "addCmd",
                    "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");

8.5版本新加內容

9.為Connector添加生命周期監聽器

digester.addObjectCreate("Server/Service/Connector/Listener",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
                    "addLifecycleListener",
                    "org.apache.catalina.LifecycleListener");

具體監聽器類由className屬性指定。默認情況下,Catalina未指定Connector監聽器。

10.為Connector添加升級協議

digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
                    "addUpgradeProtocol",
                    "org.apache.coyote.UpgradeProtocol");

用於支持HTTP/2,8.5新增。

11.添加子元素解析規則

digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

// When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",
                 new SetParentClassLoaderRule(parentClassLoader));
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

此部分指定了Servlet容器相關的各級嵌套子節點的解析規則,而且沒類嵌套子節點的解析規則封裝為一個RuleSet,包括GlobalNamingResources,Engine,Host,Context以及Cluster解析。

4.2.3.Engine的解析

Engine的解析是放在digester.addRuleSet(new EngineRuleSet("Server/Service/"));方法里面的。

具體如下

1.創建Engine實例

digester.addObjectCreate(prefix + "Engine",
                         "org.apache.catalina.core.StandardEngine",
                         "className");
digester.addSetProperties(prefix + "Engine");
digester.addRule(prefix + "Engine",
                 new LifecycleListenerRule
                         ("org.apache.catalina.startup.EngineConfig",
                          "engineConfigClass"));
digester.addSetNext(prefix + "Engine",
                    "setContainer",
                    "org.apache.catalina.Engine");

創建實例,並通過setContainer添加Service中。同時,還為Engine添加了一個生命周期的監聽器,代碼里寫死,不是通過server.xml配置的。用於打印Engine啟動和停止日志。

2.為Engine添加集群配置

digester.addObjectCreate(prefix + "Engine/Cluster",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Engine/Cluster");
digester.addSetNext(prefix + "Engine/Cluster",
                    "setCluster",
                    "org.apache.catalina.Cluster");

具體集群實現類由className屬性指定。

3.為Engine添加生命周期監聽器

digester.addObjectCreate(prefix + "Engine/Listener",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Engine/Listener");
digester.addSetNext(prefix + "Engine/Listener",
                    "addLifecycleListener",
                    "org.apache.catalina.LifecycleListener");

由server.xml配置,默認情況下,未指定Engine監聽器。

4.為Engine添加安全配置

digester.addObjectCreate(prefix + "Engine/Valve",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Engine/Valve");
digester.addSetNext(prefix + "Engine/Valve",
                    "addValve",
                    "org.apache.catalina.Valve");

為Engine添加安全配置以及攔截器Valve,具體的攔截器由className屬性指定。

4.2.4.Host的解析

Host的解析位於HostRuleSet類,digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));

1.創建Host實例

digester.addObjectCreate(prefix + "Host",
                         "org.apache.catalina.core.StandardHost",
                         "className");
digester.addSetProperties(prefix + "Host");
digester.addRule(prefix + "Host",
                 new CopyParentClassLoaderRule());
digester.addRule(prefix + "Host",
                 new LifecycleListenerRule
                         ("org.apache.catalina.startup.HostConfig",
                          "hostConfigClass"));
digester.addSetNext(prefix + "Host",
                    "addChild",
                    "org.apache.catalina.Container");

digester.addCallMethod(prefix + "Host/Alias",
                       "addAlias", 0);

創建Host實例,通過addChild()方法添加到Engine上。同時還為Host添加了一個生命周期監聽器HostConfig,同樣,該監聽器由Catalina默認添加,而不是server.xml配置。

2.為Host添加集群

digester.addObjectCreate(prefix + "Host/Cluster",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Host/Cluster");
digester.addSetNext(prefix + "Host/Cluster",
                    "setCluster",
                    "org.apache.catalina.Cluster");

配置集群,集群的配置即可以在Engine級別,也可以在Host級別。

3.為Host添加生命周期管理

digester.addObjectCreate(prefix + "Host/Listener",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Host/Listener");
digester.addSetNext(prefix + "Host/Listener",
                    "addLifecycleListener",
                    "org.apache.catalina.LifecycleListener");

此部分監聽器由server.xml配置。默認情況下,Catalina未指定Host監聽器。

4.為Host添加安全配置

digester.addObjectCreate(prefix + "Host/Valve",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Host/Valve");
digester.addSetNext(prefix + "Host/Valve",
                    "addValve",
                    "org.apache.catalina.Valve");

為Host添加安全配置以及攔截器Valve,具體攔截器類由className屬性指定。Catalina為Host默認添加的攔截器為AccessLogValve,即用於記錄訪問日志。

4.2.5.Context的解析

Context的解析,位於ContextRuleSet類,digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));

多數情況下,我們並不需要在server.xml中配置Context,而是由HostConfig自動掃描部署目錄,以context.xml文件為基礎進行解析創建。

1.創建Context實例

if (create) {
  digester.addObjectCreate(prefix + "Context",
                           "org.apache.catalina.core.StandardContext", "className");
  digester.addSetProperties(prefix + "Context");
} else {
  digester.addRule(prefix + "Context", new SetContextPropertiesRule());
}

if (create) {
  digester.addRule(prefix + "Context",
                   new LifecycleListenerRule
                   ("org.apache.catalina.startup.ContextConfig",
                    "configClass"));
  digester.addSetNext(prefix + "Context",
                      "addChild",
                      "org.apache.catalina.Container");
}

Context的解析會根據create屬性的不同而有所區別,這主要是由於Context來源於多處。通過server.xml配置Context時,create是true,因此需要創建Context實例;而通過HostConfig自動創建Context時,create為false,此時僅需要解析節點即可。

2.為Context添加生命周期監聽器

digester.addObjectCreate(prefix + "Context/Listener",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Context/Listener");
digester.addSetNext(prefix + "Context/Listener",
                    "addLifecycleListener",
                    "org.apache.catalina.LifecycleListener");

具體監聽器類由屬性className指定。

3.為Context指定類加載器

digester.addObjectCreate(prefix + "Context/Loader",
                         "org.apache.catalina.loader.WebappLoader",
                         "className");
digester.addSetProperties(prefix + "Context/Loader");
digester.addSetNext(prefix + "Context/Loader",
                    "setLoader",
                    "org.apache.catalina.Loader");

默認為org.apache.catalina.loader.WebappLoader,通過className屬性可以指定自己的實現類。

4.為Context添加會話管理器

digester.addObjectCreate(prefix + "Context/Manager",
                         "org.apache.catalina.session.StandardManager",
                         "className");
digester.addSetProperties(prefix + "Context/Manager");
digester.addSetNext(prefix + "Context/Manager",
                    "setManager",
                    "org.apache.catalina.Manager");

digester.addObjectCreate(prefix + "Context/Manager/Store",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Context/Manager/Store");
digester.addSetNext(prefix + "Context/Manager/Store",
                    "setStore",
                    "org.apache.catalina.Store");

digester.addObjectCreate(prefix + "Context/Manager/SessionIdGenerator",
                         "org.apache.catalina.util.StandardSessionIdGenerator",
                         "className");
digester.addSetProperties(prefix + "Context/Manager/SessionIdGenerator");
digester.addSetNext(prefix + "Context/Manager/SessionIdGenerator",
                    "setSessionIdGenerator",
                    "org.apache.catalina.SessionIdGenerator");

默認實現為StandardManager,同時為管理器指定會話存儲方式和會話標識生成器。Context提供了多種會話管理方式。

5.為Context添加初始化參數

digester.addObjectCreate(prefix + "Context/Parameter",
                         "org.apache.tomcat.util.descriptor.web.ApplicationParameter");
digester.addSetProperties(prefix + "Context/Parameter");
digester.addSetNext(prefix + "Context/Parameter",
                    "addApplicationParameter",
                    "org.apache.tomcat.util.descriptor.web.ApplicationParameter");

通過該配置,為Context添加初始化參數。

6.為Context添加安全配置以及web資源配置

digester.addRuleSet(new RealmRuleSet(prefix + "Context/"));

digester.addObjectCreate(prefix + "Context/Resources",
                         "org.apache.catalina.webresources.StandardRoot",
                         "className");
digester.addSetProperties(prefix + "Context/Resources");
digester.addSetNext(prefix + "Context/Resources",
                    "setResources",
                    "org.apache.catalina.WebResourceRoot");

digester.addObjectCreate(prefix + "Context/Resources/PreResources",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Context/Resources/PreResources");
digester.addSetNext(prefix + "Context/Resources/PreResources",
                    "addPreResources",
                    "org.apache.catalina.WebResourceSet");

digester.addObjectCreate(prefix + "Context/Resources/JarResources",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Context/Resources/JarResources");
digester.addSetNext(prefix + "Context/Resources/JarResources",
                    "addJarResources",
                    "org.apache.catalina.WebResourceSet");

digester.addObjectCreate(prefix + "Context/Resources/PostResources",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Context/Resources/PostResources");
digester.addSetNext(prefix + "Context/Resources/PostResources",
                    "addPostResources",
                    "org.apache.catalina.WebResourceSet");

7.為Context添加資源連接

digester.addObjectCreate(prefix + "Context/ResourceLink",
                         "org.apache.tomcat.util.descriptor.web.ContextResourceLink");
digester.addSetProperties(prefix + "Context/ResourceLink");
digester.addRule(prefix + "Context/ResourceLink",
                 new SetNextNamingRule("addResourceLink",
                                       "org.apache.tomcat.util.descriptor.web.ContextResourceLink"));

為Context添加資源鏈接ContextResourceLink,用於J2EE命名服務。

8.為Context添加Valve

digester.addObjectCreate(prefix + "Context/Valve",
                         null, // MUST be specified in the element
                         "className");
digester.addSetProperties(prefix + "Context/Valve");
digester.addSetNext(prefix + "Context/Valve",
                    "addValve",
                    "org.apache.catalina.Valve");

為Context添加攔截器Valve。

9.為Context添加守護資源配置

digester.addCallMethod(prefix + "Context/WatchedResource",
                       "addWatchedResource", 0);

digester.addCallMethod(prefix + "Context/WrapperLifecycle",
                       "addWrapperLifecycle", 0);

digester.addCallMethod(prefix + "Context/WrapperListener",
                       "addWrapperListener", 0);

digester.addObjectCreate(prefix + "Context/JarScanner",
                         "org.apache.tomcat.util.scan.StandardJarScanner",
                         "className");
digester.addSetProperties(prefix + "Context/JarScanner");
digester.addSetNext(prefix + "Context/JarScanner",
                    "setJarScanner",
                    "org.apache.tomcat.JarScanner");

digester.addObjectCreate(prefix + "Context/JarScanner/JarScanFilter",
                         "org.apache.tomcat.util.scan.StandardJarScanFilter",
                         "className");
digester.addSetProperties(prefix + "Context/JarScanner/JarScanFilter");
digester.addSetNext(prefix + "Context/JarScanner/JarScanFilter",
                    "setJarScanFilter",
                    "org.apache.tomcat.JarScanFilter");

WatchedResource標簽用於為Context添加監視資源,當這些資源發生變更時,Web應用將會被重新加載,默認為WEB-INF/web.xml

WrapperLifecycle標簽用於為Context添加一個生命周期監聽類,此類的實例並非添加到Context上,而是添加到Context包含的Wrapper上。

WrapperListener標簽用於為Context添加一個容器監聽類,此類同樣添加到Wrapper上。

JarScanner標簽用於為Context添加一個Jar掃描器。JarScanner掃描Web應用和類加載層級的Jar包。

10.為Context添加Cookie處理器

digester.addObjectCreate(prefix + "Context/CookieProcessor",
                         "org.apache.tomcat.util.http.Rfc6265CookieProcessor",
                         "className");
digester.addSetProperties(prefix + "Context/CookieProcessor");
digester.addSetNext(prefix + "Context/CookieProcessor",
                    "setCookieProcessor",
                    "org.apache.tomcat.util.http.CookieProcessor");

5.總結

本以為Digester就是對server.xml解析,能有多大的問題,不想梳理的,迫於不懂,梳理之后發現它這個設計模式還是挺棒的,用戶只需要配置節點名,規則就行。通過棧獲取元素。其他的都寫好了,只需要用就行。很棒的設計模式,應該用的是策略模式的思想,非常棒。也是有不小收獲的。

參考:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM