上善若水
          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 閱讀(7606) 評論(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 | 百家樂
          謝謝分享,學習了、  回復  更多評論
            
          主站蜘蛛池模板: 海宁市| 拜城县| 长治市| 青河县| 巴马| 仪陇县| 田东县| 汪清县| 安平县| 新化县| 滦平县| 古蔺县| 鄢陵县| 康乐县| 五寨县| 龙游县| 巴南区| 若尔盖县| 丹凤县| 察隅县| 青铜峡市| 伊宁县| 浙江省| 鄂尔多斯市| 台山市| 平顶山市| 枣庄市| 乾安县| 崇文区| 天柱县| 连江县| 萨迦县| 南阳市| 满城县| 资源县| 通辽市| 建湖县| 丹寨县| 九龙城区| 许昌市| 榆中县|