深入Jetty源碼之DescriptorProcessor實現

概述

在Jetty中,所有XML文件的配置使用Descriptor來表達,而對這些Descriptor的處理使用DescriptorProcessor來實現。


Descriptor和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方法。