概述
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相關的字段和邏輯暫不做介紹。。。。。