概述
ContextHandler繼承自ScopedHandler,它是Jetty中實(shí)現(xiàn)對(duì)一個(gè)Web Application的各種資源進(jìn)行管理,并串聯(lián)實(shí)現(xiàn)整個(gè)Servlet框架的類,比如它部分實(shí)現(xiàn)了ServletContext接口,并且在其doScope方法中為當(dāng)前Request的執(zhí)行提供了相應(yīng)的環(huán)境,如設(shè)置servletPath、pathInfo、設(shè)置ServletContext到ThreadLocal中。在Jetty中,Servlet的執(zhí)行流程和框架由ServletHandler實(shí)現(xiàn),Security框架由SecurityHandler完成,而ContextHandler主要用于實(shí)現(xiàn)環(huán)境的配置,如Request狀態(tài)的設(shè)置、ClassLoader的配置、ServletContext的實(shí)現(xiàn)等。在ContextHandler類中包含了一個(gè)Context的內(nèi)部類,用于實(shí)現(xiàn)ServletContext,而ContextHandler中的很多字段和方法也是用于實(shí)現(xiàn)ServletContext,不細(xì)述。ContextHandler實(shí)現(xiàn)
ContextHandler中doStart方法實(shí)現(xiàn):1. 使用ContextPath或DisplayName初始化logger字段;并設(shè)置當(dāng)前線程的ContextClassLoader為配置的ClassLoader實(shí)例;初始化mimeType字段;設(shè)置Context的ThreadLocal變量。
2. 初始化managedAttributes Map,并生成addBean事件;如果存在ErrorHandler,start它;生成contextInitialized事件。
3. 初始化availability字段。
4. 還原Context的ThreadLocal變量和ContextClassLoader回原有實(shí)例。
ContextHandler中doStop方法實(shí)現(xiàn):
1. 設(shè)置availability字段為STOPPED狀態(tài);初始化Context的ThreadLocal變量和ContextClassLoader為當(dāng)前Context實(shí)例以及設(shè)置的ClassLoader。
2. 生成contextDestroyed事件,以及對(duì)managedAttributes,觸發(fā)removeBean事件。
3. 還原Context的ThreadLocal變量和ContextClassLoader回原有實(shí)例。
ContextHandler中doScope方法實(shí)現(xiàn):
1. 對(duì)REQUEST、ASYNC的DispatcherType,并且是第一次進(jìn)入該ContextHandler,則如果compactPath為true,compact傳入的path,即把"http://", "http:///"等替換為"/"。
2. 對(duì)當(dāng)前請(qǐng)求做初步檢查以決定是否需要繼續(xù)執(zhí)行該請(qǐng)求(返回false表示不需要繼續(xù)執(zhí)行該請(qǐng)求):
a. 檢查availability狀態(tài),對(duì)UNAVAILABLE,發(fā)送503 Service Unavailable響應(yīng);對(duì)STOPPED、SHUTDOWN狀態(tài),或Request的handled字段為true,返回false。
b. 對(duì)設(shè)置了vhosts,檢查請(qǐng)求消息中的ServerName請(qǐng)求頭是否和vhosts中的某個(gè)vhost相同或比配,如果不成立,則返回false。
c. 檢查設(shè)置的connectors數(shù)組,如果當(dāng)前HttpConnection中的Connector.name不包含在這個(gè)設(shè)置的connectors數(shù)組中,返回false。
d. 檢查contextPath,如果target不以這個(gè)contextPath開頭或者在target中contextPath之后的字符不是"/",返回false;如果allowNullPathInfo設(shè)置為false,且target不以"/"結(jié)尾,發(fā)送"target + /"的重定向請(qǐng)求,返回false。
e. 對(duì)其他情況,返回true,表示請(qǐng)求可以繼續(xù)處理。
3. 計(jì)算pathInfo以及target為contextPath之后的路徑,并設(shè)置ContextClassLoader為當(dāng)前設(shè)置的ClassLoader。
4. 保留當(dāng)前Request的contextPath、servletPath、pathInfo信息。
5. 對(duì)任意非INCLUDE的DispatcherType,設(shè)置Request的contextPath、servletPath為null、pathInfo為傳入的target中contextPath之后的路徑。
6. 執(zhí)行nextScope的邏輯。
7. 還原當(dāng)前Request的contextPath、servletPath、pathInfo的信息。
ContextHandler中doHandle方法實(shí)現(xiàn):
1. 對(duì)有新更新Context的Request實(shí)例,向當(dāng)前Request添加注冊(cè)的ServletRequestAttributeListener,如果注冊(cè)了ServletRequestListener,生成requestInitialized事件。
2. 對(duì)REQUEST類型的DispatcherType,如果該target為保護(hù)資源(isProctedTarget,如WEB-INF、META-INF目錄下的文件),拋出404 Not Found的HttpException。
3. 執(zhí)行nextHandle()邏輯。
4. 如果注冊(cè)了ServletRequestListener,生成requestDestroyed事件,并從Request中移除當(dāng)前ContextHandler中添加的ServletRequestAttributeListener實(shí)例。
ServletContextHandler實(shí)現(xiàn)
ServletContextHandler繼承自ContextHandler類,它串連了SessionHandler、SecurityHandler和ServletHandler,在ServletContextHandler的start過程中,它會(huì)串連如下Handler:ServletContextHandler -....->SessionHandler->SecurityHandler->ServletHandler,由于ServletContextHandler、SessionHandler、ServletHandler都繼承自ScopedHandler,因而他們的執(zhí)行棧將會(huì)是:
|->ServletContextHandler.doScope()
|-> SessionHandler.doScope()
|-> ServletHandler.doScope()
|-> ServletContextHandler.doHandle()
|-> .....handler.handle()
|-> SessionHandler.doHandle()
|-> SecurityHandler.handle()
|-> ServletHandler.doHandle()
另外ServletContextHandler還提供了一個(gè)Decorator的擴(kuò)展點(diǎn),可以向ServletContextHandler注冊(cè)多個(gè)Decorator,在ServletContextHandler啟動(dòng)時(shí),它會(huì)對(duì)每個(gè)已注冊(cè)的ServletHolder和FilterHolder執(zhí)行一些額外的“裝飾”邏輯,出了對(duì)ServletHolder和FilterHolder的裝飾,它還可以裝飾Filter、Servlet、Listener等,以及在銷毀他們時(shí)加入一下自定義的邏輯:
<T extends Filter> T decorateFilterInstance(T filter) throws ServletException;
<T extends Servlet> T decorateServletInstance(T servlet) throws ServletException;
<T extends EventListener> T decorateListenerInstance(T listener) throws ServletException;
void decorateFilterHolder(FilterHolder filter) throws ServletException;
void decorateServletHolder(ServletHolder servlet) throws ServletException;
void destroyServletInstance(Servlet s);
void destroyFilterInstance(Filter f);
void destroyListenerInstance(EventListener f);
}
Decorator擴(kuò)展點(diǎn)的引入實(shí)現(xiàn)了兩種方式對(duì)Servlet、Filter、EventListener的配置:Annotation方式(AnnotationDecorator)和Plus方式(PlusDecorator),其中Annotation的方式的配置是Servlet 3.0規(guī)范中新加入的特性,而Plus方式則是Jetty提供的配置注入。
其中AnnotationDecorator的實(shí)現(xiàn)采用AnnotationInstrospector,可以向它注冊(cè)不同的InstrospectableAnnotationHandler,用以處理不同的Annotation邏輯,從而實(shí)現(xiàn)對(duì)動(dòng)態(tài)注冊(cè)的Servlet、Filter、EventListener,可以使用在它們之上的Annotation來做進(jìn)一步的配置,以簡(jiǎn)化配置本身。在Jetty中實(shí)現(xiàn)了以下幾個(gè)Annotation的InstrospectableAnnotationHandler:
@Resource => ResourceAnnotationHandler: 對(duì)類上的@Resource注解,將它作為一種資源綁定到當(dāng)前Context或Server中,對(duì)Field或Method的@Resource注解,創(chuàng)建一個(gè)Injection實(shí)例放入Context的Attribute中。在PlusDecorator中會(huì)對(duì)注冊(cè)的Injection實(shí)例做inject操作。
@Resources => ResourcesAnnotationHandler: 對(duì)類上的@Resources注解中的每個(gè)@Resource注解作為一種資源綁定到當(dāng)前Context或Server中。
@RunAs => RunAsAnnotationHandler: 關(guān)聯(lián)Servlet上@RunAs注解的值到該ServletHolder中。
@ServletSecurity => SecurityAnnotationHandler: 為@ServletSecurity注解的Servlet配置DataConstraint、Roles、methodOmission等。
@PostConstruct => PostConstructAnnotationHandler: 將有該注解的方法注冊(cè)PostConstructCallback回調(diào)類,在PlusDecorator中的decorate方法中會(huì)調(diào)用該callback。
@PreDestroy => PreDestroyAnnotationHandler: 將有該注解的方法注冊(cè)PreDestroyCallback回調(diào)類,在PlusDecorator中的decorate方法中會(huì)調(diào)用該callback。
@MultipartConfig => MultipartConfigAnnotationHandler: 將有該注解的Servlet類注冊(cè)配置的MultipartConfig信息。
@DeclareRoles => DeclareRolesAnnotationHandler: 向SecurityHandler注冊(cè)定義的Role集合。
而PlusDecorator主要處理使用以上Annotation或PlusDescriptorProcessor注冊(cè)的RunAsCollection、InjectionCollection、LifeCycleCallbackCollection的邏輯實(shí)現(xiàn)。其中RunAsCollection用于向注冊(cè)的對(duì)應(yīng)的ServletHolder注冊(cè)RunAsRole信息;InjectionCollection實(shí)現(xiàn)從JNDI中查找對(duì)應(yīng)JndiName的實(shí)例,并將其設(shè)置到Injection中指定的字段或方法中;LifeCycleCallbackCollection用于實(shí)現(xiàn)在Servlet、Filter、EventListener創(chuàng)建后或銷毀前調(diào)用相應(yīng)的有@PostConstruct注解或@PreDestroy注解的方法。
WebAppContext實(shí)現(xiàn)
WebAppContext繼承自ServletContextHandler,主要用于整合對(duì)ServletContextHandler的配置、配置WebAppClassLoader、設(shè)置war包路徑、設(shè)置contextWhiteList、保存MetaData信息等。對(duì)WebAppContext的配置,Jetty使用Configuration接口類抽象這個(gè)行為,其接口定義如下(方法名稱都比較直觀):
public void preConfigure (WebAppContext context) throws Exception;
public void configure (WebAppContext context) throws Exception;
public void postConfigure (WebAppContext context) throws Exception;
public void deconfigure (WebAppContext context) throws Exception;
public void destroy (WebAppContext context) throws Exception;
public void cloneConfigure (WebAppContext template, WebAppContext context) throws Exception;
}
在WebInfConfiguration實(shí)現(xiàn)中,在其preConfigure方法中,如果存在WEB-INF/work目錄,先在該目錄下創(chuàng)建一個(gè)名為Jetty_<host>_<port>__<resourceBase>_<contextPath>_<virtualhost><base36_hashcode_of_whole_string>的臨時(shí)目錄,然后設(shè)置WebAppContext的臨時(shí)目錄:
1. 可以手動(dòng)設(shè)置。
2. 可以使用javax.servlet.context.tempdir屬性值設(shè)置。
3. 可以設(shè)置為${jetty.home}/work/Jetty_<host>_<port>__<resourceBase>_<contextPath>_<virtualhost><base36_hashcode_of_whole_string>
4. 可以使用屬性org.eclipse.jetty.we
5. 可以設(shè)置為${java.io.tmpdir}/Jetty_<host>_<port>__<resourceBase>_<contextPath>_<virtualhost><base36_hashcode_of_whole_string>
6. 所有以上設(shè)置失敗,則使用File.createTempFile("JettyContext", "")的目錄來設(shè)置。
對(duì)于war包,如果配置了extractWAR為true,則將war包解壓到war包所在目錄的war包名的目錄或<tempDir>/webapp目錄下,如果配置了copyWebDir,則將原本配置的BaseResource下的所有內(nèi)容拷貝到<tempDir>/webapp目錄下,使用新的web_app目錄設(shè)置BaseResource目錄;如果配置了copyWebInf為true,則將WEB-INF/lib, WEB-INF/classes的兩個(gè)目錄拷貝到<tempDir>/webinf/lib, <tempDir>/webinf/classes目錄下,并更新BaseResource為原來webapp目錄和<tempDir>/webinf兩個(gè)目錄的組合;設(shè)置org.eclipse.jetty.server.webapp.ContainerIncludeJarParttern屬性,查找URLClassLoader中URL中對(duì)應(yīng)的jar包(即WebAppContext中配置的extraClassPath值),并添加到MetaData的containerJars集合中(如果不設(shè)置,則不會(huì)添加任何jar包);使用org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern屬性匹配WEB-INF/lib目錄下的所有jar包,并添加到MetaData的webInfJars集合中(如果不設(shè)置,默認(rèn)添加所有jar包)。
在其configure()方法實(shí)現(xiàn)中,設(shè)置WebAppClassLoader的classPath為WEB-INF/classes, WEB-INF/lib目錄下的所有jar包,并將這些jar包添加到WebAppClassLoader中;并且如果配置了org.eclipse.jetty.resources屬性,則將配置的List<Resource>集合添加到WebAppContext的BaseResource中。
WebXmlConfiguration的實(shí)現(xiàn),在preConfigure中,它向MetaData注冊(cè)webdefault.xml文件描述符;web.xml(默認(rèn)為WEB-INF/web.xml)文件描述符;以及override-web.xml文件描述符;在注冊(cè)過程中解析它們的absolute-ordering信息,將解析的結(jié)果合并到MetaData的ordering集合中。在configure方法實(shí)現(xiàn)中,它向MetaData添加StandardDescriptorProcessor。
MetaInfConfiguration的實(shí)現(xiàn),在preConfigure()方法中,掃描MetaData中在WebInfConfiguration中注冊(cè)的所有containerJars和webInfJars 的jar包,將找到的META-INF/web-fragment.xml生成的Resource注冊(cè)到org.eclipse.jetty.webFragments屬性中,在FragmentConfiguration中會(huì)被進(jìn)一步添加到MetaData中;將META-INF/resources/對(duì)應(yīng)的Resource注冊(cè)到org.eclipse.jetty.resources屬性中,在WebInfConfiguration的configure方法中會(huì)將這些目錄添加到BaseResource集合中;將所有*.tld文件對(duì)應(yīng)的Resource注冊(cè)到org.eclipse.jetty.tlds屬性中,在TagLibConfiguration中,會(huì)對(duì)這些注冊(cè)TLD文件做進(jìn)一步的處理。
FragmentConfiguration的實(shí)現(xiàn),在其preConfigure方法中,將MetaInfConfiguration中找到的web-fragment.xml文件對(duì)應(yīng)的Resource注冊(cè)到MetaData中,在注冊(cè)中首先解析它的ordering信息;在其configure方法中,它使用ordering中定義的順序邏輯對(duì)注冊(cè)的jar包進(jìn)行排序。
JettyWebConfiguration的實(shí)現(xiàn),在其configure方法中,依次查找jetty8-web.xml, jetty-web.xml, web-jetty.xml文件,如果有找到任意一個(gè),則使用XmlCofiguration對(duì)WebAppContext進(jìn)行配置。XmlConfiguration的實(shí)現(xiàn)參考《深入Jetty源碼之XmlConfiguration實(shí)現(xiàn)》
在AnnotationConfiguration的實(shí)現(xiàn)中,在其configure()方法中,它首先向WebAppContext中注冊(cè)AnnotationDecorator;然后它創(chuàng)建AnnotationParser實(shí)例,然后向其注冊(cè)WebServletAnnotationHandler、WebFilterAnnotationHandler、WebListenerAnnotationHandler、ClassInheritanceHandler、ContainerInitializerAnnotationHandler,它們都實(shí)現(xiàn)了DiscoverableAnnotationHandler(其中ClassInheritanceHandler實(shí)現(xiàn)的是ClassHandler接口);最后掃瞄所有ClassPath下jar包、WEB-INF/classes以及WEB-INF/lib中的jar包下的每個(gè)類,對(duì)于所有注冊(cè)為systemClasses,非serverClasses的類,使用ClassInheritanceHandler紀(jì)錄所有類包含的直接子類以及所有接口包含的直接實(shí)現(xiàn)類,而WebFilterAnnotationHandler、WebServletAnnotationHandler、WebListenerAnnotationHandler用于注冊(cè)相應(yīng)的WebFilterAnnotation、WebServletAnnotation、WebListenerAnnotation,并添加到MetaData中DiscoveredAnnotation集合中,這些DiscoveredAnnotation在MetaData的resolve方法(WebAppContext.startContext()方法中被調(diào)用)調(diào)用時(shí)會(huì)向WebAppContext注冊(cè)對(duì)應(yīng)的FilterHolder、ServletHolder、EventListener,而ContainerInitializerAnnotationHandler則會(huì)將所有注冊(cè)的注解修飾的類添加到注冊(cè)的ContainerInitializer的annotatedTypeNames集合中,該集合在ContainerInitializerConfiguration將它自身以及它的所有子類、實(shí)現(xiàn)類添加到applicableTypeNames集合中,集合之前注冊(cè)的interestedTypes的所有子類、實(shí)現(xiàn)類傳遞到ServletContainerInitializer的onStartup()方法中。
在ContainerInitializerConfiguration會(huì)使用AnnotationConfiguration中注冊(cè)ContainerInitializer實(shí)例列表,構(gòu)建applicableTypeNames,并調(diào)用其ServletContainerInitializer的onStartup方法。
EnvConfiguration實(shí)現(xiàn),在preConfigure方法中使用XmlConfiguration以及WEB-INF/jetty-env.xml文件對(duì)WebAppContext進(jìn)行配置,并且綁定JNDI環(huán)境。
TagLibConfiguration實(shí)現(xiàn),在preConfigure方法中向WebAppContext注冊(cè)TagLibListener(ServletContextListener),在TagLibListener的contextInitialized方法中,它首先查找所有能找到的web.xml中定義的*.tld文件、WEB-INF/*.tld文件、WEB-INF/tlds/*.tld文件、以及通過WebInfConfiguration在jar包中找到的*.tld文件,將每個(gè)tld文件解析成一個(gè)TldDescriptor,并且使用TldProcessor對(duì)它們進(jìn)行解析成EventListener列表,并注冊(cè)到WebAppContext中。
PlusConfiguration實(shí)現(xiàn),在preConfigure中,它向WebAppContext添加PlusDecorator;在configure方法中添加PlusDescriptorProcessor。
在WebAppContex啟動(dòng)時(shí):
1. 根據(jù)WebAppContext的allowDuplicateFragmentNames屬性設(shè)置MetaData實(shí)例對(duì)應(yīng)的屬性。
2. 調(diào)用preConfigure方法,它加載所有Configuration實(shí)例(用戶自定義或默認(rèn)設(shè)置:WebInfConfiguration、WebXmlConfiguration、MetaInfConfiguration、FragmentConfiguration、JettyWebXmlConfiguration);加載系統(tǒng)類規(guī)則集合(即不能被Web Application覆蓋的類,他們必須使用System ClassLoader加載,可以通過Server屬性org.eclipse.jetty.webapp.sysemClasses屬性定義,或者使用默認(rèn)值)以及Server類規(guī)則集合(不能被Web Application加載的類,他們需要使用System ClassLoader加載,可以使用Server屬性org.eclipse.jetty.webapp.serverClasses定義,或者使用默認(rèn)值,這兩個(gè)的區(qū)別參考WebAppClassLoader的實(shí)現(xiàn)解析);設(shè)置ClassLoader,默認(rèn)為WebAppClassLoader;調(diào)用所有Configuration的preConfigure方法。
3. 調(diào)用startContext方法,他會(huì)調(diào)用Configuration的configure方法,以及MetaData的resolve方法;在MetaData的resolve方法中,他首先設(shè)置WebAppContext的javax.servlet.context.orderedLibs屬性,然后設(shè)置ServletContext的EffectiveMajorVersion和EffectiveMinorVersion,并遍歷通過Configuration注冊(cè)的DescriptorProcessor,對(duì)webDefaultRoots、webXmlRoot、webOverrideRoots等Descriptor進(jìn)行處理,以讀取Servlet、Filter、Listener等信息的定義,遍歷在Configuration中注冊(cè)的DiscoveredAnnotation,對(duì)所有找到的WebFilter、WebServlet、WebListener注解類進(jìn)行解析并添加到WeAppContext中,最后對(duì)在FragmentConfiguration中注冊(cè)的FragmentDescriptor以及DiscoveredAnnotation進(jìn)行相應(yīng)的處理已進(jìn)一步配置WebAppContext。
4. 調(diào)用postConfiguration方法,即調(diào)用所有注冊(cè)的Configuration的postConfigure方法以做一些清理工作。
WebAppClassLoader實(shí)現(xiàn)
WebAppClassLoader是Jetty中用于對(duì)Servlet規(guī)范的ClassLoader的實(shí)現(xiàn),它集成子URLClassLoader。它不會(huì)加載任何System Class(使用System ClassLoader加載),對(duì)Java2中父ClassLoader優(yōu)先于子ClassLoader的規(guī)則,可以使用WebAppContext的setParentLoadPriority為true來配置。如果沒有配置父ClassLoader,則使用當(dāng)前的Thread Context ClassLoader,如果該ClassLoader也為null,則使用加載該類的ClassLoader,如果它還為null,則使用SystemClassLoader作為其父ClassLoader。
在加載類時(shí),WebAppClassLoader有SystemClass和ServerClass的類別,SystemClass是指在Web Application中可見的,但是他們不能被Web Application中的類(WEB-INF/classes,WEB-INF/lib中的類)覆蓋的類,而ServerClass是指這些類是Server部分的實(shí)現(xiàn),它對(duì)Web Application是不可見的,如果需要使用它們,可以將相應(yīng)的jar包添加到WEB-INF/lib中。
WebAppClassLoader默認(rèn)支持.zip,.jar為擴(kuò)展名的文件中查找class定義,可以使用org.eclipse.jetty.webapp.WebAppClassLoader.extensions系統(tǒng)屬性添加更多的擴(kuò)展名文件支持(以“,”或“;”分隔)。WebAppClassLoader也會(huì)添加WebAppContext中的ExtraClassPath到其ClassPath中(以“,”或“;”分隔),即添加URL。
在WebInfConfiguration的configure方法中,他會(huì)默認(rèn)的將所有WEB-INF/lib下的jar包以及WEB-INF/classes目錄添加到WebAppClassLoader的ClassPath中,即添加URL。
在其loadClass的實(shí)現(xiàn)中,如果某class既是SystemClass又是ServerClass,則返回null;如果不是ServerClass,且是父ClassLoader優(yōu)先或者是SystemClass,則使用父ClassLoader加載,然后再使用當(dāng)前ClassLoader加載;在getResources和getResource的實(shí)現(xiàn)中,對(duì)于ServerClass它只能從當(dāng)前ClassLoader中查找,對(duì)SystemClass它只能從父ClassLoader中查找。