上善若水
In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
posts - 146,comments - 147,trackbacks - 0

概述

Servlet是Server Applet的缩写,即在服务器端运行的小程序,而Servlet框架则是对HTTP服务器(Servlet Container)和用户小程序中间层的标准化和抽象。这一层抽象隔离了HTTP服务器的实现细节,而Servlet规范定义了各个类的行为,从而保证了这些“服务器端运行的小程序”对服务器实现的无关性(即提升了其可移植性)。
在Servlet规范有以下几个核心类(接口):
ServletContext:定义了一些可以和Servlet Container交互的方法。
Registration:实现Filter和Servlet的动态注册。
ServletRequest(HttpServletRequest):对HTTP请求消息的封装。
ServletResponse(HttpServletResponse):对HTTP响应消息的封装。
RequestDispatcher:将当前请求分发给另一个URL,甚至ServletContext以实现进一步的处理。
Servlet(HttpServlet):所有“服务器小程序”要实现了接口,这些“服务器小程序”重写doGet、doPost、doPut、doHead、doDelete、doOption、doTrace等方法(HttpServlet)以实现响应请求的相关逻辑。
Filter(FilterChain):在进入Servlet前以及出Servlet以后添加一些用户自定义的逻辑,以实现一些横切面相关的功能,如用户验证、日志打印等功能。
AsyncContext:实现异步请求处理。

Jetty中的Holder

在Jetty中,每个Servlet和其相关信息都由ServletHolder封装,并且将Servlet相关操作代理给ServletHolder;同理,对Filter也有FilterHolder与其相对应;另外ServletHolder和FilterHolder都继承自Holder实例。在Servlet 3.0中引入动态向ServletContext注册Servlet和Filter,并返回相应的Registration实例,用于进一步配置与其关联的Servlet和Filter,因而Registration也是和ServletHolde和FilterHolder相关联的接口。在Jetty中,他们的类关系图如下:

Servlet

Servlet和Filter是Servlet规范中用于定义用户逻辑实现的接口,Servlet是最初的版本,所有的“服务器端小程序”都要实现该接口,并交由Servlet Container管理其实例,负责其生命周期,以及当相应请求到来时调用相应方法。Servlet接口非常简单:
public interface Servlet {
    // Servlet Container在创建一个Servlet后调用该方法,并传入ServletConfig实例,从而用户可以在这个方法中做一些自定义的初始化工作,如初始化数据库连接等。
    // 并且Servlet Container可以保证一个Servlet实例init方法之后被调用一次,但是对一个Servlet类init方法可能会被多次调用,因而有些Servlet Container可能会在某些情况下将某些
    // Servlet移出Servlet Container,而后又重新加载这些Servlet,如为了在处理Servlet Container资源压力比较大的情况下。

    public void init(ServletConfig config) throws ServletException;

    // 返回在init方法中传入的ServletConfig实例。ServletConfig包含了在web.xml配置文件中配置当前Servlet的初始化参数,并且可以使用该ServletConfig实例获取当前ServletContext实例。
    public ServletConfig getServletConfig();
    
    // 当该Servlet对应的请求到来时,Servlet Container会调用这个Servlet的service方法,用于处理请求,并将相应写入ServletResponse参数中,该方法只能在init方法完成后被调用。
    // 在service方法中,可以选择使用ServletResponse定义的方法返回响应给客户端,或只是向ServletResponse中写入响应,最终由Servlet Container根据ServletResponse的信息将响应返回给客户端。
    // 很多情况下,Servlet Container都不会使用多线程来处理客户请求,应该该方法会在多线程环境下被使用,Servlet实现者可以实现SingleThreadMode接口以强制该方法只在单线程的环境下被使用。
    // 但是SingleThreadMode接口已经在Servlet 2.3中被废弃,实现该接口也会影响Servlet的执行性能,而且有些Servlet Container会选择实例或多个Servlet实例,以保证对请求的响应性能,因而此时依然不能保证该方法的单线程特性,因而不建议使用这个SingleThreadMode接口。

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
    
    // 返回当前Servlet的描述信息,如作者、版本号、版权等。在GenericServlet默认实现中,返回空字符串。
    public String getServletInfo();

    // 当Servlet Container销毁当前Servlet时会调用该方法,从而给Servlet提供拥有清理当前Servlet占用的资源的地方,如关闭和数据库的连接等。
    // Servlet Container会保证该方法在所有执行service方法的线程完成或超时后被调用。Servlet Container一般会在三种情况下会调用该方法:
    // 1. Servlet Container当前资源比较紧张,需要将一些不再用或不常用的Servlet移出;2. 实现某些Servlet的热部署;3. 当前Web Application被停止。

    public void destroy();
}
对每个Servlet都有一个ServletConfig实例和其对应,ServletConfig中包含了在web.xml文件中定义的Servlet的init-param参数,并且可以使用ServletConfig实例获取ServletContext实例:
public interface ServletConfig {
    // 返回该ServletConfig对应的Servlet的名称,在web.xml文件中定义的Servlet名称或对于没有注册的Servlet为其类名。
    public String getServletName();

    // 返回和其关联的ServletContext。
    public ServletContext getServletContext();
    
    // 返回在web.xml配置文件中定义或者使用ServletRegistration动态定义添加的init-param参数。
    public String getInitParameter(String name);
    public Enumeration<String> getInitParameterNames();
}
在Jetty中,不管是在web.xml中配置的Servlet还是使用ServletContext动态注册并使用ServletRegistration.Dynamic动态配置的Servlet,在ServletHandler内部都使用ServletHolder来表示一个Servlet,并且由ServletHolder来处理所有和Servlet相关的逻辑。ServletHolder的实现逻辑在之后给出。
Servlet框架中默认实现了两个Servlet:GenericServlet和HttpServlet,GenericServlet只是对Servlet的简单实现,而HttpServlet会根据请求中的方法将请求分发给相应的:doGet/doPost/doPut/doHead/doOptions/doTrace等方法,它还提供了getLastModified()方法,重写该方法用于实现条件GET。以上这些方法(除doOptions和doTrace已经有具体的逻辑实现)默认实现直接方法405 Method Not Allowed响应(Http/1.1)或404 Bad Request响应(HTTP/1.0)。重写相应的方法以实现各个Method对应的逻辑。

Filter

在Servlet 2.3开始引入了Filter机制,以在Servlet的service方法的执行前后添加一些公共的Filter逻辑,为面向切面的编程提供了很大的便利,这些公共的逻辑如纪录一个Request从进入Servlet Container到出所花费的总时间、为每个Request添加一些额外的信息以帮助之后处理、对所有或特定Request添加用户验证功能等。Filter可以在web.xml文件中定义,由Servlet Container负责其实例化、初始化以及doFilter方法的调用。在Servlet 3.0以后,还支持动态的给ServletContext注册Filter,并由返回的FilterRegistration.Dynamic实例做进一步的配置。Filter的接口定义也是比较简单:
public interface Filter {
    // 由ServletContainer在初始化一个Filter时调用,Filter的实现者可以在该方法中添加一些用户自定义的初始化逻辑,同时可以保存FilterConfig实例,它可以获取定义的init-param初始化参数以及获取ServletContext实例。其他情况和Servlet类似,不赘述。
    public void init(FilterConfig filterConfig) throws ServletException;
    
    // 每一次请求到来都会穿越配置的FilterChain,执行配置的Servlet,然后从这个FilterChain中返回。在doFilter方法的实现中,要调用下一个Filter,使用FilterChain的doFilter方法。
    // 在调用FilterChain的doFilter之前为执行请求之前的处理,而之后为请求已经执行完成,在响应返回的路上的逻辑处理。也可以步调用FilterChain的doFilter方法,以阻止请求的进一步处理。

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException;

    // 在Filter被移出Servlet Container时调用,它和Servlet类似,不赘述。**
    public void destroy();
}
Filter接口中包含对FilterConfig以及FilterChain的使用,FilterConfig和ServletConfig定义、实现以及逻辑都类似:
public interface FilterConfig {
    public String getFilterName();
    public ServletContext getServletContext();
    public String getInitParameter(String name);
    public Enumeration<String> getInitParameterNames();
}
FilterChain是Servlet中将Filter链以Channel的方式连接在一起的类,它实现了可以向执行Servlet前和后都添加一些切面逻辑,甚至阻止Servlet的执行,Filter的实现者使用FilterChain的doFilter方法调用在这个链中的下一个Filter的doFilter方法,如果当前Filter是链中最后一个Filter,则调用响应的Servlet。其接口定义如下:
public interface FilterChain {
    // 调用该方法会调用Filter链中的下一个Filter的doFilter方法,如果当前Filter是这条链中的最后一个Filter,则该方法会调用响应的Servlet的service方法。
    public void doFilter (ServletRequest request, ServletResponse response) throws IOException, ServletException;
}
在Jetty中,ServletHandler的内部类Chain实现了FilterChain接口,在构造Chain实例时,首先根据Request的URL以及对应Servlet Name查找所有相关的Filter列表,然后使用这个Filter列表、Request实例、当前请求对应的ServletHolder创建这个链,在其doFilter方法实现中,它会存储一个_filter索引,它指向下一个Filter实例,当每个Filter调用doFilter方法时,Chain会根据这个索引获取下一个Filter实例,并将该索引向后移动,从而调用下一个Filter的doFilter方法,如果这个索引值到达最后一个Filter链中的Filter,且有ServletHolder实例存在,则调用该ServletHolder的handle方法,否则调用notFound方法,即向客户端发送404 NOT FOUND响应。如果Filter不支持ASYNC模式,则在调用其doFilter之前,需要将Request的ASYNC支持设置为false。
在ServletHandler中还有CachedChain实现了FilterChain接口,它以链表的形式纪录找到的Filter列表,并将这个列表缓存在ServletHandler中,不同的dispatch类型有一个列表,并且可以根据请求的URL或请求的Servlet名来查找是否已经有缓存的Filter链表。
在ServletHandler中,可以使用setFilterChainsCached方法来配置是否使用CachedChain还是直接使用Chain,默认使用CachedChain。

Registration

Registration是Servlet 3.0规范中引入的接口,用于表示向ServletContext中动态注册的Servlet、Filter的实例,从而实现对这些动态注册的Servlet、Filter实例进行进一步的配置。 对于Servlet和Filter的配置,他们的共同点是他们有响应的Name、ClassName、Init Parameters以及asyncSupported属性,而这些方法正是Registration接口的定义。Registration接口将setAsyncSupport方法定义在其内部的Dynamic接口中,Dynamic用于表示这是用于动态的配置这个Servlet或Filter的含义,但是为什么要将这个方法放在Dynamic接口中呢?如何决定不同的方法应该是在Registration本身的接口中,而那些应该放到Dynamic接口中呢?
public interface Registration {
    // 返回这个Registration实例关联的Servlet或Filter的Name,这个Name在向ServletContext注册Servlet或Filter时给定。
    public String getName();

    // 返回这个Registration实例关联的Servlet或Filter的类名。
    public String getClassName();

    // 和这个Registration实例关联的初始化参数的操作。
    public boolean setInitParameter(String name, String value);
    public String getInitParameter(String name);
    public Set<String> setInitParameters(Map<String, String> initParameters);
    public Map<String, String> getInitParameters();

    interface Dynamic extends Registration {
        // 配置Registration关联的Servlet或Filter是否支持异步操作。
        public void setAsyncSupported(boolean isAsyncSupported);
    }
}
Registration有两个子接口:ServletRegistration和FilterRegistration,分别用于表示Servlet相关的配置和Filter相关的配置。
对ServletRegistration,它可以设置Servlet的URL Mapping、RunAsRole属性、LoadOnStartup属性等:
public interface ServletRegistration extends Registration {
    // 添加URL patterns到这个ServletRegistration关联的Servlet的映射。如果有任意的URL patterns已经映射到其他的Servlet中,则该方法不会执行任何行为。
    public Set<String> addMapping(String... urlPatterns);

    // 获取所有到当前ServletRegistration对应的Servlet的URL patterns。
    public Collection<String> getMappings();

    // 获取当前ServletRegistration对应的Servlet的RunAsRole。
    public String getRunAsRole();

    interface Dynamic extends ServletRegistration, Registration.Dynamic {
        // 设置当前ServletRegistration对应的Servlet的loadOnStartup等级。如果loadOnStartup大于或等于0,表示Servlet Container要优先初始化该Servlet,
        // 此时Servlet Container要在Container初始化时实例化并初始化该Servlet,即在所有注册的ContextListener的contextInitialized方法调用完成后。
        // 如果loadOnStartup小于0,则表示这个Servlet可以在用到的时候实例化并初始化。默认值为-1。

        public void setLoadOnStartup(int loadOnStartup);

        // 设置ServletRegistration相关的ServletSecurityElement属性。
        public Set<String> setServletSecurity(ServletSecurityElement constraint);

        // 设置ServletRegistration对应的Servlet的MultipartConfigElement属性。
        public void setMultipartConfig(MultipartConfigElement multipartConfig);

        // 设置ServletRegistration对应的Servlet的RunAsRole属性。
        public void setRunAsRole(String roleName);
    }
}
对FilterRegistration,它可以配置Filter的URL Mapping和Servlet Mapping等:
public interface FilterRegistration extends Registration {
    // 添加FilterRegistration关联的Filter到Servlet的映射,使用Servlet Name、DispatcherType作为映射。isMatchAfter表示新添加的Mapping是在已有的Mapping之前还是之后。
    public void addMappingForServletNames(EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter, String... servletNames);

    // 获取当前FilterRegistration关联的Filter已存在的到Servlet Name的映射。
    public Collection<String> getServletNameMappings();

    // 添加FilterRegistration关联的Filter到Servlet的映射,使用URL patterns、DispatcherType作为映射。isMatchAfter表示新添加的Mapping是在已有的Mapping之前还是之后。
    public void addMappingForUrlPatterns(EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter, String... urlPatterns);

    // 获取当前FilterRegistration关联的Filter已存在的到URL patterns的映射。
    public Collection<String> getUrlPatternMappings();

    interface Dynamic extends FilterRegistration, Registration.Dynamic {
    }
}
在Jetty中对Registration的实现在Holder中定义,而相应的ServletRegistration和FilterRegistration实现作为ServletHolder和FilterHolder中的内部类实现,具体参考这两个类的实现。

Holder实现

在之前有提到,在Jetty中Servlet和Filter由相应的ServletHolder和FilterHolder封装,以将Servlet/Filter相关的信息和配置放在一起,并处理各自相关的逻辑,即面向对象设计中的将数据靠近操作。由于Servlet和Filter有一些相同的配置和逻辑,因而在ServletHolder和FilterHolder中提取出了Holder父类。在Holder的实现中,它主要定义了一些Servlet和Filter都要使用的字段,比高实现了所有和InitParameter相关的操作:
    public enum Source { EMBEDDED, JAVAX_API, DESCRIPTOR, ANNOTATION };
    final private Source _source;
    protected transient Class<? extends T> _class;
    protected final Map<String,String> _initParams=new HashMap<String,String>(3);
    protected String _className;
    protected String _displayName;
    protected boolean _extInstance;
    protected boolean _asyncSupported=true;
    protected String _name;
    protected ServletHandler _servletHandler;
Holder继承自AbstractLifeCycle,它在start时,如果_class字段没有被设置,则会使用ClassLoader加载_className中指定的类实例并赋值给_class字段;Source只是目前只是一种元数据的形式存在,用于表示Servlet或Filter的来源;而extInstance用于表示Servlet和Filter实例是直接通过ServletContext注册而来,而不是在当前Holder内部创建。
在Holder类中还定了两个内部类:HolderConfig和HolderRegistration,其中HoldConfig实现了ServletConfig/FilterConfig相关的所有InitParameter相关的操作(代理给Holder);HolderRegistration实现了Registration.Dynamic接口,其实现也都代理给Holder类中的方法。

FilterHolder实现

FilterHolder实现比较简单,它直接继承自Holder类,它额外的包含了一下几个字段:
    private transient Filter _filter;
    private transient Config _config;
    private transient FilterRegistration.Dynamic _registration;
其中_filter字段在start时如果没有初始化,则使用ServletContext创建该Filter实例,而_config字段则在启动时直接创建Config实例(Config是FilterHolder的内部类,且它继承自HolderConfig,并实现了FilterConfig接口),最后调用_filter.init()方法并传入_config实例。在stop时,调用_filter.destroy()方法从而该Filter有机会做一些清理工作,并且调用ServletHandler中的destroyFilter()方法,以通知ContextHandler中定义的Decorators。在注册外部实例化的Filter时,设置_extInstance为true,同时更新_class字段,以及_name字段(如果_name字段未被设置的话)。
最后,FilterHolder中还定义了Registration内部类,它继承自HolderRegistration,并实现了FilterRegistration.Dynamic接口。该Registration内部类实现了Mapping相关方法,Jetty中使用FilterMapping来表达一个Filter的映射关系。在FilterMapping中定义了一下映射关系:
    private int _dispatches=DEFAULT;
    private String _filterName;
    private transient FilterHolder _holder;
    private String[] _pathSpecs;
    private String[] _servletNames;
它包含了两个appliesTo()方法,这两个方法在Chain用于根据dispatcherType或dispatcherType以及path计算当前FilterMapping是否匹配给定的dispatcherType或dispatcherType和path。在对dispatcherType做匹配计算时,使用FilterMapping实例的没有设置dispatcherType集合,它依然匹配REQUEST或ASYNC(如果Filter支持ASYNC模式的话)。
    boolean appliesTo(int type) {
        if (_dispatches==0)
            return type==REQUEST || type==ASYNC && _holder.isAsyncSupported();
        return (_dispatches&type)!=0;
    }

ServletHolder实现

ServletHolder实现相对复杂,它继承自Holder类,并实现了UserIdentity.Scope接口以及Comparable接口,其中Comparable接口用于当ServletHandler在start时,对注册的所有ServletHolder的数组排序以决定他们的start顺序。它包含了一下额外的字段:
    private int _initOrder;
    private boolean _initOnStartup=false;
    private Map<String, String> _roleMap;
    private String _forcedPath;
    private String _runAsRole;
    private RunAsToken _runAsToken;
    private IdentityService _identityService;
    private ServletRegistration.Dynamic _registration;
    private transient Servlet _servlet;
    private transient Config _config;
    private transient long _unavailable;
    private transient UnavailableException _unavailableEx;
    public static final Map<String,String> NO_MAPPED_ROLES = Collections.emptyMap();
其中_initOrder为ServletRegistration.Dynamic接口的实现,它定义ServletHolder在ServletHandler中的start顺序,即compareTo()方法的实现的主要参考信息。在Jetty中,只要设置了loadOnStart字段,则它就会在start时被初始化(即使设置的值为负数)。在设置外部Servlet实例直接到ServletHolder中时,_extInstance字段为被设置为true。如果在web.xml中的Servlet定义中有jsp-file定义,则设置该ServletHolder的forcePath值为该jsp-file中定义的值,而其className的值为servlet-class的定义,此时在ServletHolder的handle方法中会将forcePath的值设置到Request的org.apache.catalina.jsp_file属性中(这个设置用来干嘛呢?还不了解。。。);在ServletHolder启动时,如果Servlet实例实现了SingleThreadModel接口,则Servlet实例使用SingleThreadedWrapper类来表示(它包含一个Servlet栈 ,对每次请求,它会复用以前已经创建的Servlet实例或者创建一个新的实例以处理当前的请求,即该Servlet会有多个实例),如果_extInstance为true或者配置了loadOnStart属性,则在ServletHolder启动是就会初始化这个Servlet(实例化Servlet,并调用Servlet的init方法)。当一个请求到来时,ServletHolder调用其handle方法以处理该请求,在该方法中调用Servlet的service方法,如果在处理过程中出错了,则在Request中设置javax.servlet.error.servlet_name属性为ServletHolder的Name属性。

类似FilterMapping,Jetty也使用ServletMapping表达Servlet和URL pattern的映射关系:
    private String[] _pathSpecs;
    private String _servletName;

其他Security相关的字段和逻辑暂不做介绍。。。。。
posted on 2014-05-11 23:52 DLevin 阅读(7548) 评论(3)  编辑  收藏 所属分类: Jetty

FeedBack:
# re: 深入Jetty源码之Servlet框架及实现(Servlet、Filter、Registration)
2014-05-12 09:35 | 百家乐
看的头有点晕,谢谢楼主的分享、  回复  更多评论
  
# re: 深入Jetty源码之Servlet框架及实现(Servlet、Filter、Registration)
2014-05-13 00:59 | 非凡娱乐
楼主辛苦了!  回复  更多评论
  
# re: 深入Jetty源码之Servlet框架及实现(Servlet、Filter、Registration)
2014-05-16 10:22 | 百家乐
谢谢分享,学习了、  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: