本文共 5689 字,大约阅读时间需要 18 分钟。
在Jetty中,所有XML文件的配置使用Descriptor来表达,而对这些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方法。
转载地址:http://vygxx.baihongyu.com/