上善若水
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();