深入Jetty源码之Servlet框架及实现(Servlet、Filter、Registration)
概述
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接口非常简单:
// 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实例:
// 返回该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的接口定义也是比较简单:
// 由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 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。其接口定义如下:
// 调用该方法会调用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接口中呢?
// 返回这个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属性等:
// 添加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等:
// 添加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相关的操作:
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 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 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模式的话)。
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 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 _servletName;
其他Security相关的字段和逻辑暂不做介绍。。。。。