天天看點

深入Jetty源碼之DescriptorProcessor實作

在jetty中,所有xml檔案的配置使用descriptor來表達,而對這些descriptor的處理使用descriptorprocessor來實作。

深入Jetty源碼之DescriptorProcessor實作

descriptor實作

descriptor可以表達一個*.tld檔案(tlddescriptor)、一個/meta-inf/web.xml檔案(webdescriptor),一個/org/eclipse/jetty/webapp/webdefault.xml(defaultsdescriptor),一個/meta-inf/web-fragment.xml檔案(fragmentdescriptor),一個override-web.xml檔案(overridedescriptor)。其中tlddescriptor在taglibconfiguration的taglistener中查找并使用tldprocessor解析;webdescriptor在webxmlconfiguration的preconfigure中查找,并設定到metadata的webxmlroot字段中,并更新metadata的ordering字段,其資源檔案可以手動設定webappcontext的descriptor字段,或者未設定而使用meta-inf/web.xml檔案;defaultsdescriptor也在webxmlconfiguration的preconfigure中查找,并設定到metadata的webdefaultsroot字段中,并更新metadata的ordering字段,其資源檔案可以手動設定webappcontext中的defaultsdescriptor字段,或未設定而預設使用/org/eclipse/jetty/webapp/webdefault.xml檔案;overridedescriptor也在webxmlconfiguration的preconfigure中查找,并設定到metadata的weboverrideroots集合中,并更新metadata中的ordering字段,其資源檔案可以手動設定,如果未設定,則忽略;而fragmentdescriptor則是在fragmentconfiguration中的preconfigure中添加到metadata的webfragmentresourcemap、webfragmentnamemap以及webfragmentroots中,如果metadata的ordering為null,且不為absolute,則更新ordering字段。

每個descriptor使用一個xml的resource執行個體作為構造函數建構,并使用xmlparser将其解析成類dom樹,儲存樹的root節點引用。

除了tlddescriptor在taglibconfiguration中已經處理完成,其他的descriptor使用standarddescriptorprocessor以及plusdescriptorprocessor來處理,其中standarddescriptorprocessor在webxmlconfiguration的configure方法中注冊到metadata的descriptorprocessors集合中,而plusdescriptorprocessor在plusconfiguration的configure方法中注冊到metadata中。并在matadata的resolve方法中使用注冊的descriptorprocessor依次解析webdefaultsroot、webxmlroot、weboverrideroots以及webfragmentroots對應的descriptor執行個體。

descriptorprocessor實作

descriptorprocessor隻有一個process方法,他周遊傳入的descriptor的所有node,并對不同node做相應的處理。在iterativedescriptorprocessor的采用了非常巧妙的實作方法,即使用一個visitors的map,包含節點的tag到相應處理方法的映射,因而在iterativedescriptorprocessor的實作中,它周遊descriptor的節點樹,對每個節點查找對應的處理方法,并調用查找到的方法,其子類的實作隻需要注冊這個visitors的map,然後實作注冊的方法即可;為了增加可擴充性,在解析前和解析後分别添加了start、end的插入點。

如在standarddescriptorprocessor中,注冊了如下幾個visitor方法:

context-param => visitcontextparam 向webappcontext添加initparam資訊。

display-name => visitdisplayname 向webappcontext設定displayname屬性。

servlet => visitservlet 向servlethandler中添加一個新的servletholder,并配置其servlet-name、init-param、servlet-class、jsp-file、load-on-startup、security-role-ref、run-as、async-supported、enabled、multipart-config等資訊;如果id設定為jsp,則會在initparam中配置scratchdir、classpath參數,以及為jasper配置com.sun.appserv.jsp.classpath參數,而在webappcontext中為jasper配置org.apache.catalina.jsp_classpath屬性;用于注冊org.apache.jasper.servlet.jspservlet;對jsp-file,設定其forcepath為該值。

servlet-mapping=> visitservletmapping 配置servlethandler中servlet-name對應的servletmapping資訊。

session-config => visitsessionconfig 設定sessionhandler中sessionmanager的一些配置資訊。

mime-mapping => visitmimemapping 設定webappcontext中extension到mimetype的映射。

welcome-file-list => visitwelcomefilelist 設定webappcontext中的welcomefiles。

locale-encoding-mapping-list => visitlocaleencodinglist 設定webappcontext中locale到encoding的映射關系。

error-page => visiterrorpage 設定errorpageerrorhandler中errorcode或exceptiontype到location的映射關系。

taglib => visittaglib 設定taglib-uri到taglib-location的映射關系,即webappcontext中taglib-uri是taglib-location的alias。

jsp-config => visitjspconfig 将jsp-property-group下url-pattern映射到jspservlet中。

security-constraint => visitsecurityconstraint 向securityhandler中添加constraintmapping。

login-config => visitloginconfig 向securityhandler中設定authmethod、realmname屬性,以及對form方法的驗證,設定login、error頁面的initparam。

security-role => visitsecurityrole 向securityhandler中注冊定義的role集合。

filter => visitfilter 向servlethandler注冊filterholder,并配置filter-name、filter-class、init-param、async-supported等資訊。

filter-mapping => visitfiltermapping 向servlethandler注冊filtermapping資訊。

listenr => visitlistener 向webappcontext注冊eventlistener。

distributable => visitdestributable 設定webdescriptor的distributable屬性為true。

在plusdescriptorprocessor中,首先在其start方法中會向webappcontext注冊injectioncollection、lifecyclecallbackcollection、runascollection(該屬性在runasannotationhandler中使用)屬性,并且注冊了以下幾個visitor方法:

env-entry => visitenventry 向injectioncollection添加injection執行個體,其中jndiname為env-entry-name定義的值,valueclass為env-entry-type定義的類型,而targetclass、targetname為injection-target下的injection-target-class、injection-target-name中定義的值,每個injection-target生成一個injection執行個體。同時将env-entry-value中定義的值綁定到java:com/env/<name>對應的資源中。(injection執行個體也可以使用@resource注解注冊,并在resourceannotationhandler中解析)

resource-ref => visitresourceref 向injectioncollection添加injection執行個體,其中jndiname為res-ref-name,typeclass為res-type,并綁定該引用資源。

resource-env-ref => visitresourceenvref 向injectioncollection添加injection執行個體,其中jndiname為resource-env-ref-name,typeclass為resource-env-ref-type,并綁定該env引用資源。

message-destination-ref => visitmessagedestinationref 向injectioncollection添加injection執行個體,其中jndiname為message-destination-ref-name,typeclass為message-destination-type,并綁定該message-destination引用資源。

post-construct => visitpostconstruct 向lifecyclecallbackcollection注冊一個postconstructcallback,其targetclass由lifecycle-callback-class定義,而method由lifecycle-callback-method定義(該postconstructcallback也可以使用@postconstruct的annotation方式注冊,并在postconstructannotationhandler中解析)。

pre-destroy => visitpredestroy 向lifecyclecallbackcollection注冊predestroycallback,其targetclass由lifecycle-callback-class定義,methodname由lifecycle-callback-method定義(該predestroycallback也可以使用@predestroy注解注冊,并在predestroyannotationhandler中解析)。

所有以上注冊的runascollection、injectioncollection、lifecyclecallbackcollection都在plusdecorator中使用,plusdecorator類實作decorator方法,在所有的decorate實作方法中,使用runascollection向servletholder中注冊配置的rolename(感覺這裡有bug,應該是decorate一個servlet而不是servletholder);使用injectioncollection向servlet、filter、eventlistener注入jndi對應的值;使用lifecyclecallbackcollection調用所有注冊的postconstruct方法。而在destroyservlet、filter執行個體時,使用lifecyclecallbackcollection調用素有注冊的predestroy方法。