深入Jetty源码之ContextHandler

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介:

概述

ContextHandler继承自ScopedHandler,它是Jetty中实现对一个Web Application的各种资源进行管理,并串联实现整个Servlet框架的类,比如它部分实现了ServletContext接口,并且在其doScope方法中为当前Request的执行提供了相应的环境,如设置servletPath、pathInfo、设置ServletContext到ThreadLocal中。在Jetty中,Servlet的执行流程和框架由ServletHandler实现,Security框架由SecurityHandler完成,而ContextHandler主要用于实现环境的配置,如Request状态的设置、ClassLoader的配置、ServletContext的实现等。在ContextHandler类中包含了一个Context的内部类,用于实现ServletContext,而ContextHandler中的很多字段和方法也是用于实现ServletContext,不细述。

ContextHandler实现

ContextHandler中doStart方法实现:
1. 使用ContextPath或DisplayName初始化logger字段;并设置当前线程的ContextClassLoader为配置的ClassLoader实例;初始化mimeType字段;设置Context的ThreadLocal变量。
2. 初始化managedAttributes Map,并生成addBean事件;如果存在ErrorHandler,start它;生成contextInitialized事件。
3. 初始化availability字段。
4. 还原Context的ThreadLocal变量和ContextClassLoader回原有实例。

ContextHandler中doStop方法实现:
1. 设置availability字段为STOPPED状态;初始化Context的ThreadLocal变量和ContextClassLoader为当前Context实例以及设置的ClassLoader。
2. 生成contextDestroyed事件,以及对managedAttributes,触发removeBean事件。
3. 还原Context的ThreadLocal变量和ContextClassLoader回原有实例。

ContextHandler中doScope方法实现:
1. 对REQUEST、ASYNC的DispatcherType,并且是第一次进入该ContextHandler,则如果compactPath为true,compact传入的path,即把"//", "///"等替换为"/"。
2. 对当前请求做初步检查以决定是否需要继续执行该请求(返回false表示不需要继续执行该请求):
    a. 检查availability状态,对UNAVAILABLE,发送503 Service Unavailable响应;对STOPPED、SHUTDOWN状态,或Request的handled字段为true,返回false。
    b. 对设置了vhosts,检查请求消息中的ServerName请求头是否和vhosts中的某个vhost相同或比配,如果不成立,则返回false。
    c. 检查设置的connectors数组,如果当前HttpConnection中的Connector.name不包含在这个设置的connectors数组中,返回false。
    d. 检查contextPath,如果target不以这个contextPath开头或者在target中contextPath之后的字符不是"/",返回false;如果allowNullPathInfo设置为false,且target不以"/"结尾,发送"target + /"的重定向请求,返回false。
    e. 对其他情况,返回true,表示请求可以继续处理。
3. 计算pathInfo以及target为contextPath之后的路径,并设置ContextClassLoader为当前设置的ClassLoader。
4. 保留当前Request的contextPath、servletPath、pathInfo信息。
5. 对任意非INCLUDE的DispatcherType,设置Request的contextPath、servletPath为null、pathInfo为传入的target中contextPath之后的路径。
6. 执行nextScope的逻辑。
7. 还原当前Request的contextPath、servletPath、pathInfo的信息。

ContextHandler中doHandle方法实现:
1. 对有新更新Context的Request实例,向当前Request添加注册的ServletRequestAttributeListener,如果注册了ServletRequestListener,生成requestInitialized事件。
2. 对REQUEST类型的DispatcherType,如果该target为保护资源(isProctedTarget,如WEB-INF、META-INF目录下的文件),抛出404 Not Found的HttpException。
3. 执行nextHandle()逻辑。
4. 如果注册了ServletRequestListener,生成requestDestroyed事件,并从Request中移除当前ContextHandler中添加的ServletRequestAttributeListener实例。

ServletContextHandler实现

ServletContextHandler继承自ContextHandler类,它串连了SessionHandler、SecurityHandler和ServletHandler,在ServletContextHandler的start过程中,它会串连如下Handler:
ServletContextHandler -....->SessionHandler->SecurityHandler->ServletHandler,由于ServletContextHandler、SessionHandler、ServletHandler都继承自ScopedHandler,因而他们的执行栈将会是:
|->ServletContextHandler.doScope()
  |-> SessionHandler.doScope()
    |-> ServletHandler.doScope()
      |-> ServletContextHandler.doHandle()
        |-> .....handler.handle()
          |-> SessionHandler.doHandle()
            |-> SecurityHandler.handle()
              |-> ServletHandler.doHandle()

另外ServletContextHandler还提供了一个Decorator的扩展点,可以向ServletContextHandler注册多个Decorator,在ServletContextHandler启动时,它会对每个已注册的ServletHolder和FilterHolder执行一些额外的“装饰”逻辑,出了对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扩展点的引入实现了两种方式对Servlet、Filter、EventListener的配置:Annotation方式(AnnotationDecorator)和Plus方式(PlusDecorator),其中Annotation的方式的配置是Servlet 3.0规范中新加入的特性,而Plus方式则是Jetty提供的配置注入。
其中AnnotationDecorator的实现采用AnnotationInstrospector,可以向它注册不同的InstrospectableAnnotationHandler,用以处理不同的Annotation逻辑,从而实现对动态注册的Servlet、Filter、EventListener,可以使用在它们之上的Annotation来做进一步的配置,以简化配置本身。在Jetty中实现了以下几个Annotation的InstrospectableAnnotationHandler:
@Resource          => ResourceAnnotationHandler: 对类上的@Resource注解,将它作为一种资源绑定到当前Context或Server中,对Field或Method的@Resource注解,创建一个Injection实例放入Context的Attribute中。在PlusDecorator中会对注册的Injection实例做inject操作。
@Resources         => ResourcesAnnotationHandler: 对类上的@Resources注解中的每个@Resource注解作为一种资源绑定到当前Context或Server中。
@RunAs              => RunAsAnnotationHandler: 关联Servlet上@RunAs注解的值到该ServletHolder中。
@ServletSecurity => SecurityAnnotationHandler: 为@ServletSecurity注解的Servlet配置DataConstraint、Roles、methodOmission等。
@PostConstruct    => PostConstructAnnotationHandler: 将有该注解的方法注册PostConstructCallback回调类,在PlusDecorator中的decorate方法中会调用该callback。
@PreDestroy        => PreDestroyAnnotationHandler: 将有该注解的方法注册PreDestroyCallback回调类,在PlusDecorator中的decorate方法中会调用该callback。
@MultipartConfig  => MultipartConfigAnnotationHandler: 将有该注解的Servlet类注册配置的MultipartConfig信息。
@DeclareRoles     => DeclareRolesAnnotationHandler: 向SecurityHandler注册定义的Role集合。
而PlusDecorator主要处理使用以上Annotation或PlusDescriptorProcessor注册的RunAsCollection、InjectionCollection、LifeCycleCallbackCollection的逻辑实现。其中RunAsCollection用于向注册的对应的ServletHolder注册RunAsRole信息;InjectionCollection实现从JNDI中查找对应JndiName的实例,并将其设置到Injection中指定的字段或方法中;LifeCycleCallbackCollection用于实现在Servlet、Filter、EventListener创建后或销毁前调用相应的有@PostConstruct注解或@PreDestroy注解的方法。

WebAppContext实现

WebAppContext继承自ServletContextHandler,主要用于整合对ServletContextHandler的配置、配置WebAppClassLoader、设置war包路径、设置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方法自定义当前支持的Configuration集合,Jetty默认添加集合有:WebInfConfiguration、WebXmlConfiguration、MetaInfConfiguration、FragmentConfiguration、JettyWebXmlConfiguration,另外Jetty内部默认实现的还有:AnnotationConfiguration、ContainerInitializerConfiguration、EnvConfiguration、PlusConfiguration、TagLigConfiguration等。

在WebInfConfiguration实现中,在其preConfigure方法中,如果存在WEB-INF/work目录,先在该目录下创建一个名为Jetty_<host>_<port>__<resourceBase>_<contextPath>_<virtualhost><base36_hashcode_of_whole_string>的临时目录,然后设置WebAppContext的临时目录:
1. 可以手动设置。
2. 可以使用javax.servlet.context.tempdir属性值设置。
3. 可以设置为${jetty.home}/work/Jetty_<host>_<port>__<resourceBase>_<contextPath>_<virtualhost><base36_hashcode_of_whole_string>
4. 可以使用属性org.eclipse.jetty.we 
pasting
 bapp.basetempdir指定的base,然后设置为${base}/Jetty_<host>_<port>__<resourceBase>_<contextPath>_<virtualhost><base36_hashcode_of_whole_string>
5. 可以设置为${java.io.tmpdir}/Jetty_<host>_<port>__<resourceBase>_<contextPath>_<virtualhost><base36_hashcode_of_whole_string>
6. 所有以上设置失败,则使用File.createTempFile("JettyContext", "")的目录来设置。
对于war包,如果配置了extractWAR为true,则将war包解压到war包所在目录的war包名的目录或<tempDir>/webapp目录下,如果配置了copyWebDir,则将原本配置的BaseResource下的所有内容拷贝到<tempDir>/webapp目录下,使用新的web_app目录设置BaseResource目录;如果配置了copyWebInf为true,则将WEB-INF/lib, WEB-INF/classes的两个目录拷贝到<tempDir>/webinf/lib, <tempDir>/webinf/classes目录下,并更新BaseResource为原来webapp目录和<tempDir>/webinf两个目录的组合;设置org.eclipse.jetty.server.webapp.ContainerIncludeJarParttern属性,查找URLClassLoader中URL中对应的jar包(即WebAppContext中配置的extraClassPath值),并添加到MetaData的containerJars集合中(如果不设置,则不会添加任何jar包);使用org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern属性匹配WEB-INF/lib目录下的所有jar包,并添加到MetaData的webInfJars集合中(如果不设置,默认添加所有jar包)。
在其configure()方法实现中,设置WebAppClassLoader的classPath为WEB-INF/classes, WEB-INF/lib目录下的所有jar包,并将这些jar包添加到WebAppClassLoader中;并且如果配置了org.eclipse.jetty.resources属性,则将配置的List<Resource>集合添加到WebAppContext的BaseResource中。

WebXmlConfiguration的实现,在preConfigure中,它向MetaData注册webdefault.xml文件描述符;web.xml(默认为WEB-INF/web.xml)文件描述符;以及override-web.xml文件描述符;在注册过程中解析它们的absolute-ordering信息,将解析的结果合并到MetaData的ordering集合中。在configure方法实现中,它向MetaData添加StandardDescriptorProcessor。

MetaInfConfiguration的实现,在preConfigure()方法中,扫描MetaData中在WebInfConfiguration中注册的所有containerJars和webInfJars 的jar包,将找到的META-INF/web-fragment.xml生成的Resource注册到org.eclipse.jetty.webFragments属性中,在FragmentConfiguration中会被进一步添加到MetaData中;将META-INF/resources/对应的Resource注册到org.eclipse.jetty.resources属性中,在WebInfConfiguration的configure方法中会将这些目录添加到BaseResource集合中;将所有*.tld文件对应的Resource注册到org.eclipse.jetty.tlds属性中,在TagLibConfiguration中,会对这些注册TLD文件做进一步的处理。

FragmentConfiguration的实现,在其preConfigure方法中,将MetaInfConfiguration中找到的web-fragment.xml文件对应的Resource注册到MetaData中,在注册中首先解析它的ordering信息;在其configure方法中,它使用ordering中定义的顺序逻辑对注册的jar包进行排序。

JettyWebConfiguration的实现,在其configure方法中,依次查找jetty8-web.xml, jetty-web.xml, web-jetty.xml文件,如果有找到任意一个,则使用XmlCofiguration对WebAppContext进行配置。XmlConfiguration的实现参考 《深入Jetty源码之XmlConfiguration实现》

在AnnotationConfiguration的实现中,在其configure()方法中,它首先向WebAppContext中注册AnnotationDecorator;然后它创建AnnotationParser实例,然后向其注册WebServletAnnotationHandler、WebFilterAnnotationHandler、WebListenerAnnotationHandler、ClassInheritanceHandler、ContainerInitializerAnnotationHandler,它们都实现了DiscoverableAnnotationHandler(其中ClassInheritanceHandler实现的是ClassHandler接口);最后扫瞄所有ClassPath下jar包、WEB-INF/classes以及WEB-INF/lib中的jar包下的每个类,对于所有注册为systemClasses,非serverClasses的类,使用ClassInheritanceHandler纪录所有类包含的直接子类以及所有接口包含的直接实现类,而WebFilterAnnotationHandler、WebServletAnnotationHandler、WebListenerAnnotationHandler用于注册相应的WebFilterAnnotation、WebServletAnnotation、WebListenerAnnotation,并添加到MetaData中DiscoveredAnnotation集合中,这些DiscoveredAnnotation在MetaData的resolve方法(WebAppContext.startContext()方法中被调用)调用时会向WebAppContext注册对应的FilterHolder、ServletHolder、EventListener,而ContainerInitializerAnnotationHandler则会将所有注册的注解修饰的类添加到注册的ContainerInitializer的annotatedTypeNames集合中,该集合在ContainerInitializerConfiguration将它自身以及它的所有子类、实现类添加到applicableTypeNames集合中,集合之前注册的interestedTypes的所有子类、实现类传递到ServletContainerInitializer的onStartup()方法中。

在ContainerInitializerConfiguration会使用AnnotationConfiguration中注册ContainerInitializer实例列表,构建applicableTypeNames,并调用其ServletContainerInitializer的onStartup方法。

EnvConfiguration实现,在preConfigure方法中使用XmlConfiguration以及WEB-INF/jetty-env.xml文件对WebAppContext进行配置,并且绑定JNDI环境。

TagLibConfiguration实现,在preConfigure方法中向WebAppContext注册TagLibListener(ServletContextListener),在TagLibListener的contextInitialized方法中,它首先查找所有能找到的web.xml中定义的*.tld文件、WEB-INF/*.tld文件、WEB-INF/tlds/*.tld文件、以及通过WebInfConfiguration在jar包中找到的*.tld文件,将每个tld文件解析成一个TldDescriptor,并且使用TldProcessor对它们进行解析成EventListener列表,并注册到WebAppContext中。

PlusConfiguration实现,在preConfigure中,它向WebAppContext添加PlusDecorator;在configure方法中添加PlusDescriptorProcessor。 

在WebAppContex启动时:

1. 根据WebAppContext的allowDuplicateFragmentNames属性设置MetaData实例对应的属性。

 

2. 调用preConfigure方法,它加载所有Configuration实例(用户自定义或默认设置:WebInfConfiguration、WebXmlConfiguration、MetaInfConfiguration、FragmentConfiguration、JettyWebXmlConfiguration);加载系统类规则集合(即不能被Web Application覆盖的类,他们必须使用System ClassLoader加载,可以通过Server属性org.eclipse.jetty.webapp.sysemClasses属性定义,或者使用默认值)以及Server类规则集合(不能被Web Application加载的类,他们需要使用System ClassLoader加载,可以使用Server属性org.eclipse.jetty.webapp.serverClasses定义,或者使用默认值,这两个的区别参考WebAppClassLoader的实现解析);设置ClassLoader,默认为WebAppClassLoader;调用所有Configuration的preConfigure方法。

3. 调用startContext方法,他会调用Configuration的configure方法,以及MetaData的resolve方法;在MetaData的resolve方法中,他首先设置WebAppContext的javax.servlet.context.orderedLibs属性,然后设置ServletContext的EffectiveMajorVersion和EffectiveMinorVersion,并遍历通过Configuration注册的DescriptorProcessor,对webDefaultRoots、webXmlRoot、webOverrideRoots等Descriptor进行处理,以读取Servlet、Filter、Listener等信息的定义,遍历在Configuration中注册的DiscoveredAnnotation,对所有找到的WebFilter、WebServlet、WebListener注解类进行解析并添加到WeAppContext中,最后对在FragmentConfiguration中注册的FragmentDescriptor以及DiscoveredAnnotation进行相应的处理已进一步配置WebAppContext。

4. 调用postConfiguration方法,即调用所有注册的Configuration的postConfigure方法以做一些清理工作。

WebAppClassLoader实现

WebAppClassLoader是Jetty中用于对Servlet规范的ClassLoader的实现,它集成子URLClassLoader。它不会加载任何System Class(使用System ClassLoader加载),对Java2中父ClassLoader优先于子ClassLoader的规则,可以使用WebAppContext的setParentLoadPriority为true来配置。如果没有配置父ClassLoader,则使用当前的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部分的实现,它对Web Application是不可见的,如果需要使用它们,可以将相应的jar包添加到WEB-INF/lib中。

 

WebAppClassLoader默认支持.zip,.jar为扩展名的文件中查找class定义,可以使用org.eclipse.jetty.webapp.WebAppClassLoader.extensions系统属性添加更多的扩展名文件支持(以“,”或“;”分隔)。WebAppClassLoader也会添加WebAppContext中的ExtraClassPath到其ClassPath中(以“,”或“;”分隔),即添加URL。

 

在WebInfConfiguration的configure方法中,他会默认的将所有WEB-INF/lib下的jar包以及WEB-INF/classes目录添加到WebAppClassLoader的ClassPath中,即添加URL。

 

在其loadClass的实现中,如果某class既是SystemClass又是ServerClass,则返回null;如果不是ServerClass,且是父ClassLoader优先或者是SystemClass,则使用父ClassLoader加载,然后再使用当前ClassLoader加载;在getResources和getResource的实现中,对于ServerClass它只能从当前ClassLoader中查找,对SystemClass它只能从父ClassLoader中查找。


相关文章
|
缓存 分布式计算 API
Spark Netty与Jetty (源码阅读十一)
  spark呢,对Netty API又做了一层封装,那么Netty是什么呢~是个鬼。它基于NIO的服务端客户端框架,具体不再说了,下面开始。   创建了一个线程工厂,生成的线程都给定一个前缀名。      像一般的netty框架一样,创建Netty的EventLoopGroup:      在常用...
1094 0