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