StandardWrapper


Tomcat中有四種類型的Servlet容器,分別是 Engine、Host、Context、Wrapper,每個Wrapper實例表示一個具體的Servlet定義,StandardWrapper就是Catalina中的Wrapper接口的標准實現.

方法調用序列:指的是每當服務器接收到Http請求時,服務器中調用的一系列方法,對於每個引入的HTTP請求,連接器都會調用與其關聯的Servlet容器的 invoke方法,然后,Servlet容器會調用其所有子容器的invoke方法,

具體的過程看下面:

  1. 連接器創建request和response對象;
  2. 連接器調用StandardContext實例的invoke方法,
  3. 接着StandardContext實例的invoke方法會調用其管道對象的額invoke方法,StandardContext對象的基礎閥是StandardContextValue類的實例,因此StandardContext的管道對象會調用其基礎閥的invoke方法,
  4. StandardContextValue實例的invoke方法會獲取響應的Wrapper實例來處理HTTP請求,調用Wrapper實例的invoke方法
  5. StandardWrapper類是Wrapper接口的標准實現,StandardWrapper對象會調用其管道對象的invoke方法。
  6. StandardWrapper對象的基礎閥是StandardWrapperValue類的實例,因此會調用StandardWrapperValue的invoke方法,其invoke方法會調用Wrapper實例的allocate方法獲取servlet實例;
  7. allocate方法會調用load方法載入相應的servlet類,若已經載入,咋無需重復載入,
  8. load方法會調用servlet實例的init方法
  9. StandWrapperValue調用Servlet實例的service方法

注意:StandardContext和StandardWrapper兩個類的構造函數都設置了響應的基礎閥作為其基礎閥,

SingleThreadModel

servlet類可以實現javax.servlet.SingleThreadModel接口,這樣的Servlet類與被稱為STM Servlet類,根據Servlet規范,實現此接口的目的是保證Servlet類實例一次只能處理一個請求,下面給出點Servlet2.規范中,SRV.14.2.21一節的內容

若Servlet類實現了SingleThreadModel接口,則可以保證絕不會有兩個線程同時執行該Servlet實例的service方法,這一點由Servlet容器通過控制對單一Servlet實例的同步訪問實現,或者維護一個Servlet實例池,然后將每個新請求分派給一個空閑的Servlet實例

該接口並不能方法之Servlet訪問共享資源造成的同步問題,例如訪問類的靜態變量或訪問Servlet作用域之外的類,

事實上 實現了SingleThreadModel接口的Servlet類只能保證在同一時刻,只有一個線程在執行該Servlet實例的service方法,但是為了提高執行性能,Servlet容器會創建多個同一(Servlet類)的STM Servlet實例,也就說,STM Servlet實例的service方法會在多個STM Servlet實例中並發的執行,如果Servlet實例需要訪問靜態類變量或類外的某些資源的話,就有可能引起同步問題。

多線程的虛假安全性

在Servlet 2.4 規范中,SingleThreadModel接口已經棄用了,因為他會使Servlet程序員誤以為實現了該接口的Servlet類就是多線程安全的,但是Servlet2.3 和 2.4規范還對該接口提供了支持。

 

StandardWrapper

StandardWrapper對象主要任務是載入它多代表的servlet類,並進行實例化,但是,StandardWrapper類並不調用 Servlet類的service方法,該任務是由StandardWrapperValue對象(StandardWrapper實例管道對象的的基礎閥)完成的,StandardWrapperValue對象通過調用與其關聯的StandardWrpper類的allocate方法從StandardWrapper實例中獲取Servlet實例,在獲得了Servlet實例之后,StandardWrapperValue實例就會調用 Servlet實例的service方法,。

當第一次請求某個Servlet類時,StandardWrapper載入Servlet類,由於StandardWrapper實例會動態的載入該Servlet類,因此,它必須知道該Servlet類的完全限定名,可以調用StandardWrapper的setServletClass方法指定該servlet類的完全限定名,也可以調用其setName方法為該servlet類指定一個名字,

置於當StandardWrapperValue實例請求 加載 Servlet實例時,,StandardWrapper實例必須考慮到該Servlet類是否實現了SingleThreadModel接口,對於那些沒有實現SingleThreadModel接口的servlet類,StandardWrapper只會載入該servlet類一次,並對隨后的請求都返回該servlet類的同一個實例,StandardWrapper實例不需要多個servlet實例,因為它假設該servlet類的service方法在多線程環境中是線程安全的,如果必要的話,由servlet程序員來負責同步對共享資源的訪問,

面對一個STM servlet類,事情就不同了,StandardWrapper實例必須保證每個時刻只能有一個線程在執行STM servlet實例的 service方法,如果StandardWrapper實例只維護一個STM servlet實例的話,下面是可能出現調用STM servlet實例的 service方法的代碼

1  Servlet instance = wrapper.allocate();//從wrapper中獲取了一個 servlet實例
2         if((instance  instanceof SingleThreadModel)){
3             synchronized (instance) {
4                 instance.service(request, response);
5             }
6         }else{
7             instance.service(request, response);
8         }

但是為了獲得更好的性能,StandardWrapper實例會維護一個STM servlet實例池,Wrapper實例負責准備一個javax.servlet.servletConfig實例,后者在servlet實例內部可以獲取到,

分配servlet實例

StandardWrapper實例的invoke方法會調用Wrapper實例的allocate方法獲取請求的servlet的一個實例,因此StandardWrapper要實現allocate方法,給大家看下 其方法簽名

 1 /**
 2      * 
 3      * 分配這個Servlet的初始化實例,該實例准備調用它的<code>service()</code>方法。如果servlet類沒有實現
 4      * <code>SingleThreadModel</code>, 則可以立即返回(唯一的 后續再有請求該servlet的請求
 5      * 不會再創建新的實例)初始化實例。如果servlet類實現<code>SingleThreadModel</code>,
 6      * 則Wrapper實現必須確保這個實例在通過調用<code>deallocate()</code>釋放之前不會被再次分配。
 7      *
 8      * @exception ServletException
 9      *                如果servlet init()方法拋出異常
10      * 
11      * @exception ServletException
12      *                如果發生加載錯誤
13      */
14     public Servlet allocate() throws ServletException {

注意:allocate方法返回請求servlet的一個實例,因為要支持 STM servlet,allocate方法需要變得復雜一點,事實上為了處理 STM servlet類 和 非STM servlet,allocate方法分為兩個部分,第一部分

 1 if (!singleThreadModel) {  //返回一個 一個 非STM servlet的實例  } 

布爾變量 singleThreadModel用來標明該StandardWrapper實例標志的servlet類是否是STM servlet,該變量的初始值是false,LoadServlet方法會檢查它正在載入的Servlet類是不是一個STM Servlet類,並根據結果修改變量singleThreadModel的值,

注意()

下面看下第一部分 和第二部分

對於非STM Servlet類,StandardWrapper類定義了一個 名為 instance,類型為 javax.servlet.Servlet的變量

/**
     * 若該 {@code StandardWrapper} 實例 所代表的Servlet 為非({@link SingleThreadModel}
     * Servlet)類, 則代表的該Servlet 只會被創建一次 ,{@code instance}存儲只創建一次的Servlet實例
     */
    private Servlet instance = null;

allocate方法 會檢查變量 instance 是否是 null,若是 則allocate方法調用LoadServlet方法載入相關的Servlet類,然后 將整型變量 countAllocated的值加1,並返回 instance的值代碼如下

 1 // 如果不是SingleThreadedModel,每次返回相同的實例
 2         if (!singleThreadModel) {
 3             // 返回一個 一個 非STM servlet的實例
 4 
 5             // Load and initialize our instance if necessary
 6             if (instance == null) {
 7                 synchronized (this) {
 8                     if (instance == null) {
 9                         try {
10                             instance = loadServlet();
11                         } catch (ServletException e) {
12                             throw e;
13                         } catch (Throwable e) {
14                             throw new ServletException(sm.getString("standardWrapper.allocate"), e);
15                         }
16                     }
17                 }
18             }
19 
20             if (!singleThreadModel) {
21                 if (debug >= 2)
22                     log("  Returning non-STM instance");
23                 //將當前活動的Servlet數 加1 
24                 countAllocated++;
25                 return (instance);
26             }
27 
28         }

若StandardWrppper表示的Servlet類是一個STM Servlet類,則allocate方法會試圖從對象池中返回一個Servlet實例,變量 instancePool是一個 java.util.Stack類型的棧,其中保存了所有的STM Servlet實例,

1    /**
2      * instancePool是一個 {@link java.util.Stack}類型的棧,其中保存了所有的STM Servlet實例
3      */
4     private Stack instancePool = null;

該變量在LoadServlet方法中初始化

只要STM Servlet實例數量不超過指定的最大數,allocate會返回一個STM Servlet實例。整型變量 maxInstances保存了在棧中存儲的STM Servlet實例的最大值,默認值是20

  /** * 整型變量 maxInstances 保存了在棧中存儲的STM Servlet實例的最大值,默認值是20  */  private int maxInstances = 20; 

為了跟蹤當前 wrapper中 STM Servlet實例的數量,StandardWrapper類使用整型變量 nInstances來保存這個數值。

/**
     * 為了跟蹤當前 wrapper中 STM Servlet實例的數量,StandardWrapper類使用整型變量 nInstances來保存這個數值。
     */
    private int nInstances = 0;

下面是allocate方法的第二部分

 

 1 synchronized (instancePool) {
 2             // 為毛這樣寫呢 因為 就算該Wrapper代表的是 STM servlet,第一次執行alllocate方法的時候
 3             // 布爾變量 singleThreadModel 的值 是為false的,它會執行上面第一部分 非 STM servlet加載流程,
 4             // 在其調用loadServlet方法是時 其方法內部會根據 是否是繼承了 SingleThreadModel接口來
 5             // 修改布爾變量singleThreadModel的值
 6             // 所以初始到這里時 為 countAllocated = 0 nInstances = 0;
 7             // 然后在while循環中直到 STM Servlet實例的數量與要大於 countAllocated 循環中 只會 將
 8             // nInstances的數量增加 而 countAllocated是在循環結束后增加
 9             while (countAllocated >= nInstances) {
10                 // 如果可能的話,分配一個新的實例,或者等待
11                 if (nInstances < maxInstances) {
12                     // 如果當前活動的STM Servlet實例數量 沒有達到允許的最大上限 則 創建新的 Servlet實例
13                     // 並將其加入到 STM Servlet 實例池中
14                     try {
15                         instancePool.push(loadServlet());
16                         // 將活動的STM Servlet實例數量 加 1
17                         nInstances++;
18                     } catch (ServletException e) {
19                         throw e;
20                     } catch (Throwable e) {
21                         throw new ServletException(sm.getString("standardWrapper.allocate"), e);
22                     }
23                 } else {
24                     try {
25                         // 若 當前活動的 STM servlet實例 數量已經達到了最大值 則將線程掛起 等待 有被用完的 STM
26                         // serlvlet實例 被放回到棧中
27                         instancePool.wait();
28                     } catch (InterruptedException e) {
29                         ;
30                     }
31                 }
32             }
33             if (debug >= 2)
34                 log("  Returning allocated STM instance");
35             // 將當前StandardWrapper中活動的 servlet實例數加一
36             countAllocated++;
37             // 並將 棧頂的STM servlet實例 取出
38             return (Servlet) instancePool.pop();
39 
40         }

載入Servlet類

StandardWrapper實例實現了 Wrapper接口的 load方法,load方法調用 loadServlet方法載入某個Servlet類,並調用其init方法,此時要傳入一個 javax.servlet.ServletConfig實例作為參數,下面展示一下 loadServlet方法是怎么工作的

loadServlet方法首先會檢查當前的StandardWrapper類是否表示的是一個STM Servlet類,若不是 且 變量 instance不為null (表示以前已經載入過這個Servlet類)。它就直接返回該實例

 1 /**
 2      * 
 3      * 
 4      * 如果還沒有至少一個初始化的實例,則加載並初始化此servlet的實例。例如,這可以用於加載部署描述符中標記的、
 5      * 要在服務器啟動時加載的servlet。
 6      */
 7     public synchronized Servlet loadServlet() throws ServletException {
 8 
 9         // 檢查該StandardWrapper類表示的是否是一個STM Servlet類,若不是,且 instance變量
10         // 不為null(代表之前載入過這個servlet類),直接返回該實例
11         if (!singleThreadModel && (instance != null))
12             return instance;

若instance 為 null 或者 該servlet實例是一個STM servlet,則執行后續的方法,、

首先,它會獲取System.out和 System.error的輸出,便於它使用javax.servlet.ServletContext的log方法記錄日志信息

 1 PrintStream out = System.out; 2 SystemLogHandler.startCapture(); 

然后它定義類型為javax.servler.Servlet名為servlet的變量,變量servlet表示已經載入的servlet實例,該實例將會有loadServlet方法返回

// 變量servlet表示已經載入的servlet實例,該實例將會有loadServlet方法返回
        Servlet servlet = null;

LoadServlet方法負責載入該Servlet類,原先類名會保存在類變量 ServletClass中,現在loadServlet方法要將變量名寫入到字符串actualClass中;

//LoadServlet方法負責載入該Servlet類,原先類名會保存在類變量 ServletClass中,現在loadServlet方法要將變量名寫入到字符串actualClass中;
            String actualClass = servletClass;

但是由於Catalina也是一個JSP容器,因此loadServlet方法必須檢查請求的Servlet是不是一個jsp頁面,若是,LoadServlet方法需要獲取代表該JSP頁面的實際的servlet類;

1             //因為Catalina也是一個JSP容器,因此loadservlet方法必須檢查請求的servlet是不是一個JSP頁面,若是 loadServlet方法需要獲取代表該JSP頁面的實際servlet類
2             if ((actualClass == null) && (jspFile != null)) {
3                 Wrapper jspWrapper = (Wrapper) ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
4                 if (jspWrapper != null)
5                     actualClass = jspWrapper.getServletClass();
6             }

如果找不到該JSP頁面的實際的servlet類,則會使用變量 servletClass的值,但是若是沒有調用StandardWrapper類的setServletClass方法設置ServletClass的值,則會拋出異常,並停止執行后續運行的方法

1 // 如果找不到該JSP頁面的實際的servlet類,則會使用變量
2             // servletClass的值,但是若是沒有調用StandardWrapper類的setServletClass方法設置ServletClass的值,則會拋出異常,並停止執行后續運行的方法
3             if (actualClass == null) {
4                 unavailable(null);
5                 throw new ServletException(sm.getString("standardWrapper.notClass", getName()));
6             }

這時 要載入的Servlet類名已經解析完成,loadServlet方法會獲取載入器,若找不到載入器則它會拋出異常,方法終止

1 // 這時 要載入的Servlet類名已經解析完成,loadServlet方法會獲取載入器,若找不到載入器則它會拋出異常,方法終止
2             Loader loader = getLoader();
3             if (loader == null) {
4                 unavailable(null);
5                 throw new ServletException(sm.getString("standardWrapper.missingLoader", getName()));
6             }

若可以找到載入器,則loadServlet方法會調用載入器的getClassLoader方法獲取一個ClassLoader;

// 若可以找到載入器,則loadServlet方法會調用載入器的getClassLoader方法獲取一個ClassLoader;
            ClassLoader classLoader = loader.getClassLoader();

在org.apache.catalina包下,Catalina提供了一寫用於訪問Servlet容器內部數據的專用Servlet類,如果某個Servlet類時這種專用的Servlet,即 isContainerProviededServlet方法返回true,則變量classLoader的賦值為另外一種ClassLoader實例,如此一來,

這個通過特殊classLoader加載出來的Servlet實例就可以訪問Catalina內部數據了。

1             // 在org.apache.catalina包下,Catalina提供了一寫用於訪問Servlet容器內部數據的專用Servlet類,如果某個Servlet類時這種專用的Servlet,即
2             // isContainerProviededServlet方法返回true,則變量classLoader的賦值為另外一種ClassLoader實例,如此一來,
3             // 這個通過特殊classLoader加載出來的Servlet實例就可以訪問Catalina內部數據了。
4             if (isContainerProvidedServlet(actualClass)) {
5                 classLoader = this.getClass().getClassLoader();
6                 log(sm.getString("standardWrapper.containerServlet", getName()));
7             }

有了載入器和准備載入的Servlet類名之后,loadServlet方法就可以載入Servlet類了

 1 //有了載入器和准備載入的Servlet類名之后,loadServlet方法就可以載入Servlet類了
 2             Class classClass = null;
 3             try {
 4                 if (classLoader != null) {
 5                     System.out.println("Using classLoader.loadClass");
 6                     classClass = classLoader.loadClass(actualClass);
 7                 } else {
 8                     System.out.println("Using forName");
 9                     classClass = Class.forName(actualClass);
10                 }
11             } catch (ClassNotFoundException e) {
12                 unavailable(null);
13                 throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass), e);
14             }
15             if (classClass == null) {
16                 unavailable(null);
17                 throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass));
18             }

然后實例化該Servlet類

 1 //然后 實例化該Servlet類
 2             try {
 3                 servlet = (Servlet) classClass.newInstance();
 4             } catch (ClassCastException e) {
 5                 unavailable(null);
 6                 // Restore the context ClassLoader
 7                 throw new ServletException(sm.getString("standardWrapper.notServlet", actualClass), e);
 8             } catch (Throwable e) {
 9                 unavailable(null);
10                 // Restore the context ClassLoader
11                 throw new ServletException(sm.getString("standardWrapper.instantiate", actualClass), e);
12             }

在實例化之后 立即檢查該Servlet是否允許載入,若不允許立即終止

1 // 實例化Servlet類之后 會立即檢查該Servlet類是否允許載入 若不允許載入 終止 
2             if (!isServletAllowed(servlet)) {
3                 throw new SecurityException(sm.getString("standardWrapper.privilegedServlet", actualClass));
4             }

若通過了安全檢查,它會繼續檢查該Servlet類是否是一個ContainerServlet類型的Servlet,實現了org.apache.catalina.ContainerServlet接口的Servlet可以訪問Catalina的內部功能,若該servlet類是一個ContainerServlet,loadServlet方法會調用

ContainerServlet接口的setWrapper方法,傳入這個StandardWrapper實例。

1 // 若通過了安全檢查,它會繼續檢查該Servlet類是否是一個ContainerServlet類型的Servlet,實現了org.apache.catalina.ContainerServlet接口的Servlet可以訪問Catalina的內部功能,若該servlet類是一個ContainerServlet,loadServlet方法會調用
2             // ContainerServlet接口的setWrapper方法,傳入這個StandardWrapper實例。
3             if ((servlet instanceof ContainerServlet) && isContainerProvidedServlet(actualClass)) {
4                 System.out.println("calling setWrapper");
5                 ((ContainerServlet) servlet).setWrapper(this);
6                 System.out.println("after calling setWrapper");
7             }

接下來,loadServlet方法觸發 BEFORE_INIT_EVENT事件,調用Servlet實例的init方法

1 // 接下來,loadServlet方法觸發 BEFORE_INIT_EVENT事件,調用Servlet實例的init方法
2             try {
3                 instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet);
4                 //facade 為 javax.servlet.ServletConfig對象的一個外觀變量
5                 servlet.init(facade);

繼續展示下一部分代碼前 先引入一個 變量的解釋 loadOnStartup

 

我們在web.xml中配置servlet的時候會有個屬性<load-on-startup></load-on-startup>,這里主要記一下它的作用,源碼在后續記得好好看一下。

The load-on-startup element indicates that this servlet should be loaded (instantiated and have its init() called) on the startup of the web application. The optional contents of these element must be an integer indicating the order in which the servlet should be loaded. If the value is a negative integer, or the element is not present, the Container is free to load the servlet whenever it chooses.   If the value is a positive integer or 0, the container must load and initialize the servlet as the application is deployed. The container must guarantee that servlets marked with lower integers are loaded before servlets marked with higher integers. The container may choose the order of loading of servlets with the same load-on-start-up value.
意思大概:

    1. load-on-startup 元素標記容器是否應該在web應用程序啟動的時候就加載這個servlet,(實例化並調用其init()方法)。
    2. 它的值必須是一個整數,表示servlet被加載的先后順序。
    3. 如果該元素的值為負數或者沒有設置,則容器會當Servlet被請求時再加載。
    4. 如果值為正整數或者0時,表示容器在應用啟動時就加載並初始化這個servlet,值越小,servlet的優先級越高,就越先被加載。值相同時,容器就會自己選擇順序來加載。

初始值為-1;

 

 1 /**
 2      * 
 3      * StandardWrapper代表的servlet的啟動時加載順序值(負值表示第一次調用時的加載)。
 4      * <p>
 5      * <b>load-on-startup</b>
 6      * 元素標記容器是否應該在web應用程序啟動的時候就加載這個servlet,(實例化並調用其init()方法)。
 7      * 它的值必須是一個整數,表示servlet被加載的先后順序。 如果該元素的值為負數或者沒有設置,則容器會當Servlet被請求時再加載。
 8      * 如果值為正整數或者0時,表示容器在應用啟動時就加載並初始化這個servlet,值越小,servlet的優先級越高,就越先被加載。值相同時,
 9      * 容器就會自己選擇順序來加載
10      * </p>
11      */
12     private int loadOnStartup = -1;

 

 

 

若變量 loadOnstartup的值大於0,而被請求的Servlet類實際上是一個jsp頁面,則也調用Servlet的service方法

1 // 若變量 loadOnstartup的值大於0,而被請求的Servlet類實際上是一個jsp頁面,則也調用Servlet的service方法
2                 if ((loadOnStartup > 0) && (jspFile != null)) {
3                     // Invoking jspInit
4                     HttpRequestBase req = new HttpRequestBase();
5                     HttpResponseBase res = new HttpResponseBase();
6                     req.setServletPath(jspFile);
7                     req.setQueryString("jsp_precompile=true");
8                     servlet.service(req, res);
9                 }

接下來loadServlet方法會觸發AFTER_INIT_EVENT事件

    //接下來loadServlet方法會觸發AFTER_INIT_EVENT事件
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet);

若StandardWrapper對象表示的Servlet類是一個STM servlet, 將SingleThreadModel類變量賦值為 true否則false 然后將該Servlet實例添加到STM servlet實例池中,因此會先判斷變量 instancePool的值是否為null,若是則要給它賦值一個Stack對象:

1             // 若StandardWrapper對象表示的Servlet類是一個STM servlet,
2             // 將SingleThreadModel類變量賦值為 true否則false 然后將該Servlet實例添加到STM
3             // servlet實例池中,因此會先判斷變量 instancePool的值是否為null,若是則要給它賦值一個Stack對象:
4             singleThreadModel = servlet instanceof SingleThreadModel;
5             if (singleThreadModel) {
6                 if (instancePool == null)
7                     instancePool = new Stack();
8             }
9             fireContainerEvent("load", this);

在finally代碼塊中,loadServlet方法停止捕獲System.out 和 System.error對象,記錄在早日ServletContext的log方法過程中產生的日志消息

 1 finally {
 2             //停止捕獲System.out 和 System.err對象,記錄在載入ServletContext的log方法的過程中產生的日志消息
 3             String log = SystemLogHandler.stopCapture();
 4             if (log != null && log.length() > 0) {
 5                 if (getServletContext() != null) {
 6                     getServletContext().log(log);
 7                 } else {
 8                     out.println(log);
 9                 }
10             }
11         }

最后返回已經載入的Servlet類

return servlet;

 ServletConfig對象

StandardWrapper類的LoadServlet在載入Servlet類后,會調用該Servlet實例的init方法,init方法需要傳入一個javax.servlet.ServletConfig實例作為參數,那這個ServletCongfig 是咋被StandardWrapper類獲取的捏,請看

 1 /**
 2  * 
 3  * <p>
 4  * <b>Title:StandardWrapper.java</b>
 5  * </p>
 6  * <p>
 7  * Copyright:ChenDong 2018
 8  * </p>
 9  * <p>
10  * Company:僅學習時使用
11  * </p>
12  * <p>
13  * 類功能描述: 表示單個servlet定義的Wrapper接口的標准實現。不允許子容器,父容器必須是{@link Context}。
14  * </p>
15  * 
16  * @author 陳東
17  * @date 2018年12月1日 下午2:57:33
18  * @version 1.0
19  */
20 public final class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper {

 

因為它本身就已經繼承了 javax.servlet.ServletConfig接口,那么下面給大家展示一下ServletConfig接口的4個方法;

  1. getServletContext()
  2. getServletName()
  3. getInitParameter
  4. getInitParameterNames()

注意:StandardWrapper 類並不會將自身傳遞給Servlet實例的init方法,它會在一個StandardWrapperFacade實例中包裝自身,將絕大多數的公共方法對 Servlet程序員隱藏起來。

先說下第一個方法:getServletContext方法 先看下方法簽名xt getServletContext();

public ServletContext getServletContext() 

StandardWrapper實例肯定是StandardContext實例的子容器,也就是說StandardWrapper實例的父容器就是StandardContext實例,對於StandardContext對象來說,可以直接調用自身的getServletContext方法來獲得一個ServletContext實例,下面是StandardWrapper中該方法的具體實現

 1 /**
 2      * 返回與此{@link Servlet}關聯的{@link ServletContext }上下文.
 3      */
 4     public ServletContext getServletContext() {
 5 
 6         if (parent == null)
 7             return (null);
 8         else if (!(parent instanceof Context))
 9             return (null);
10         else
11             return (((Context) parent).getServletContext());
12 
13     }

注意:從上面的代碼也是可以看出 ,無法單獨使用一個Wrapper實例來表示一個servlet類的定義,Wrapper實例必須存在於某個Context容器中,這樣當調用其父容器的getServletContext方法時才能返回ServletContext類的一個實例

第二個方法 getServletName()

該方法返回Servlet類的名字,方法簽名如下

java.lang.String getServletName();

那么在StandardWrapper中的實現如下

1 /**
2      * 返回這個Servlet的名字.
3      */
4     public java.lang.String getServletName() {
5 
6         return (getName());
7 
8     }

該方法僅僅是簡單調用了ContainerBase類(StandardWrapper的父類)的getName方法,ContainerBase類的 getName方法實現如下

public String getName() {

        return (name);

    }

可以通過setName方法設置變量name的值,通過傳遞Servlet的名稱可以調用StandardWrapper實例的setName方法。

第三個方法 getInitParameter方法

該方法返回指定初始參數的值,該方法簽名如下:

java.lang.String getInitParameter(java.lang.String name);

在StandardWrapper中,初始化參數存儲在一個HashMap類型的名為parameters的變量中

1     /**
2      * 
3      * 存儲初始化參數的HashMap類型的變量,key值為參數名稱
4      */
5     private HashMap parameters = new HashMap();

那么這個parameters是怎么填充值得呢?調用StandardWrapper類的addInitParameter方法,並傳入參數的名字和對應的值來填充parameters的值

 1 /**
 2      *
 3      * 向存儲初始化參數的parameters變量中 添加一對新的初始化參數
 4      *
 5      * @param name
 6      *            要添加的初始化參數名稱 Name of this initialization parameter to add
 7      * @param value
 8      *            要添加的初始參數的值
 9      */
10     public void addInitParameter(String name, String value) {
11 
12         synchronized (parameters) {
13             parameters.put(name, value);
14         }
15         fireContainerEvent("addInitParameter", name);
16 
17     }

那么StandardWrapper 中getInitParameter()方法的實現如下

 1 /**
 2      *
 3      *
 4      * 返回指定名稱的初始化參數值(如果有的話);否則返回<code>null</code>.
 5      * 
 6      * @param name
 7      *            要檢索的初始化參數的名稱
 8      * 
 9      * 
10      */
11     public String getInitParameter(String name) {
12 
13         return (findInitParameter(name));
14 
15     }

其中findInitParameter方法接收一個指定的初始化參數名的字符串變量,調用HashMap變量 parameters的get方法獲取初始化參數的值,下面是具體實現

 1 /**
 2      * 返回指定名稱的初始化參數值(如果有的話);否則返回<code>null</code>.
 3      *
 4      * @param name
 5      *            要檢索的初始化參數的名稱
 6      */
 7     public String findInitParameter(String name) {
 8 
 9         synchronized (parameters) {
10             return ((String) parameters.get(name));
11         }
12 
13     }

第四個方法:getInitParameterNames()方法

該方法返回所有初始化參數的名字的集合,實際上是一個枚舉類型java.util.Enumeraton的實例,下面是該方法的簽名

java.util.Enumeration getInitParameterNames();

那么在看下StandardWrapper中的具體實現

 1 /**
 2      * 返回為該servlet定義的初始化參數名稱集。如果沒有定義,則返回空枚舉。
 3      */
 4     public Enumeration getInitParameterNames() {
 5 
 6         synchronized (parameters) {
 7             return (new Enumerator(parameters.keySet()));
 8         }
 9 
10     }

其中 Enumerator實現了java.util.Enumeration接口,

  1 package org.apache.catalina.util;
  2 
  3 import java.util.Collection;
  4 import java.util.Enumeration;
  5 import java.util.Iterator;
  6 import java.util.Map;
  7 import java.util.NoSuchElementException;
  8 
  9 /**
 10  * 
 11  * <p>
 12  * <b>Title:Enumerator.java</b>
 13  * </p>
 14  * <p>
 15  * Copyright:ChenDong 2018
 16  * </p>
 17  * <p>
 18  * Company:僅學習時使用
 19  * </p>
 20  * <p>
 21  * 類功能描述:
 22  * 圍繞Java2集合類對象Iterator包裝枚舉的適配器類,以便現有的返回枚舉的API可以輕松地在新集合上運行。提供構造函數來容易地創建這樣的包裝器。
 23  * </p>
 24  * 
 25  * @author 26  * @date 2018年12月1日 下午4:01:40
 27  * @version 1.0
 28  */
 29 public final class Enumerator implements Enumeration {
 30 
 31     // ----------------------------------------------------------- Constructors
 32 
 33     /**
 34      * 
 35      * 在指定集合的值上返回枚舉。
 36      *
 37      * @param collection
 38      *            要返回枚舉值的集合
 39      */
 40     public Enumerator(Collection collection) {
 41 
 42         this(collection.iterator());
 43 
 44     }
 45 
 46     /**
 47      * 在指定迭代器返回的值上返回枚舉。
 48      *
 49      * 
 50      * 
 51      * @param iterator
 52      *            要被包裝的迭代器
 53      */
 54     public Enumerator(Iterator iterator) {
 55 
 56         super();
 57         this.iterator = iterator;
 58 
 59     }
 60 
 61     /**
 62      * 在指定映射的所有value返回枚舉
 63      *
 64      * @param map
 65      *            要返回枚舉值得映射
 66      */
 67     public Enumerator(Map map) {
 68 
 69         this(map.values().iterator());
 70 
 71     }
 72 
 73     // ----------------------------------------------------- Instance Variables
 74 
 75     /**
 76      * 
 77      * 這個類所表示的<code>Enumeration</code>實際操作的<code>Iterator</code>。
 78      */
 79     private Iterator iterator = null;
 80 
 81     // --------------------------------------------------------- Public Methods
 82 
 83     /**
 84      * 
 85      * 檢查該枚舉是否包含更多元素
 86      * 
 87      * 
 88      * @return 當且僅當該枚舉對象包含要提供的至少一個元素,則為<code>true</code>,否則為<code>true</code>
 89      */
 90     public boolean hasMoreElements() {
 91 
 92         return (iterator.hasNext());
 93 
 94     }
 95 
 96     /**
 97      * 如果該枚舉要提供至少一個元素,則返回該枚舉的下一個元素.
 98      *
 99      * @return 這個枚舉的下一個元素
100      *
101      * @exception NoSuchElementException
102      *                如果沒有更多的元素存在
103      */
104     public Object nextElement() throws NoSuchElementException {
105 
106         return (iterator.next());
107 
108     }
109 
110 }

 Servlet容器的父子關系

Wrapper實例代表一個Servlet實例,因此Wrapper實例不能再有子容器了,不應該在調用其addChild方法,否則拋出java.lang.IllegalStateException異常,下面是StandardWrapper類中的addChild方法的實現;

 1 /**
 2      * Wrapper實例代表一個Servlet實例,因此Wrapper實例不能再有子容器了,不應該在調用其addChild方法, 否則拋出{@code
 3      * java.lang.IllegalStateException}異常,下面是{@link StandardWrapper}類中的
 4      * {@code addChild}方法的實現;
 5      *
 6      * @param child
 7      *            要被添加的子容器
 8      */
 9     public void addChild(Container child) {
10 
11         throw new IllegalStateException(sm.getString("standardWrapper.notChild"));
12 
13     }

說完了子容器 也就是Wrapper 不能有兒子,但是人家是可以有老子滴,當然了Wrapper的父容器只能是Context類的實現,若是在調用Wrapper實例的setParaent方法時傳入了一個非Context類型的容器,則會拋出

java.lang.IllegalAumentException異常

 1 /**
 2      * 
 3      *
 4      * <dd>為當前的Container也就是StandardWrapper 設置父容器,其父容器只能是Context類的實現</dd>
 5      * 
 6      * 
 7      * @param container
 8      *            要設置的父容器
 9      * @exception IllegalArgumentException
10      *                傳入了一個非Context類型的容器
11      */
12     public void setParent(Container container) {
13 
14         if ((container != null) && !(container instanceof Context))
15             throw new IllegalArgumentException(sm.getString("standardWrapper.notContext"));
16         super.setParent(container);
17 
18     }

 StandardWrapperFacade

下面來談一下 StandardWrapperFacade類 也就是 StandardWrapper的外觀類,

咱們上面說到過,StandardWrapper實例會調用它所代表的Servlet實例的init方法,init方法需要一個javax.servlet.ServletConfig實例,而StandardWrapper類本身就實現了 javax.servlet.ServletConfig接口,所以理論上StandardWrapper可以將自己傳入Servlet的init方法中,

但是StandardWrapper類要將其大部分公共方法對Servlet程序員隱藏起來,為了實現這個目的StandardWrapper類將自身實例包裝成StandardWrapperFacade類的一個實例,這個其實就是外觀類的設計模式,有興趣可以學習一下,

StandardWrapper 與 StandardWrapperFacade 都共同繼承了javax.servlet.ServletConfig接口,(就是想要使用的是一個什么樣的外觀類 就讓它繼承什么接口 這樣外觀類就有了需要使用的全部方法,然后外觀類中會有一個聲明成接口類型 原類私有實例 變量,且還有接口方法,方法的全部實現都調用原類的方法),那么下面就詳細展示一體

StandardWrapper類使用下面的代碼來創建StandardWrapperFacade類的一個實例,需要將自身的實例作為參數傳入到StandardWrapperFacade類的構造函數中:

1    /**
2      * 該{@link StandardWrapper}類的 {@link ServletConfig}類型的外觀類
3      */
4     private StandardWrapperFacade facade = new StandardWrapperFacade(this);

StandardWrapperFacde類提供了一個私有 ServletConfig類型的類級別的變量 config 來保存原類的實例

   /**
     * 保存 該外觀類封裝的 原類
     */
    private ServletConfig config = null;

當在StandardWrapper對象內部創建StandardWrapperFacde類的一個實例時,StandardWrapperFacade類的構造函數需要傳入StandardWrapper對象,並未變量 config賦值

1     /**
2      * 創建一個包裝了 指定 {@link StandardWrapper} 的外觀類
3      */
4     public StandardWrapperFacade(StandardWrapper config) {
5 
6         super();
7         this.config = (ServletConfig) config;
8 
9     }

因此在StandardWrapper 中 調用 Servlet 的 init方法時,會傳入一個同樣實現了javax.servlet.ServletConfig接口的 StandardWrapperFacade的一個實例,這樣在Servlet實例內調用ServletConfig類的方法的時候,StandardWrapperFacade實例會直接調用 包裝的StandardWrapper類的相應方法;

 

 1 package org.apache.catalina.core;
 2 
 3 import java.util.Enumeration;
 4 import javax.servlet.ServletConfig;
 5 import javax.servlet.ServletContext;
 6 
 7 /**
 8  * 
 9  * <p>
10  * <b>Title:StandardWrapperFacade.java</b>
11  * </p>
12  * <p>
13  * Copyright:ChenDong 2018
14  * </p>
15  * <p>
16  * Company:僅學習時使用
17  * </p>
18  * <p>
19  * 類功能描述: {@link StandardWrapper }類的 {@link ServletConfig }類型的外觀類
20  * </p>
21  * 
22  * @author 陳東
23  * @date 2018年12月1日 下午5:12:01
24  * @version 1.0
25  */
26 public final class StandardWrapperFacade implements ServletConfig {
27 
28     // ----------------------------------------------------------- Constructors
29 
30     /**
31      * 創建一個包裝了 指定 {@link StandardWrapper} 的外觀類
32      */
33     public StandardWrapperFacade(StandardWrapper config) {
34 
35         super();
36         this.config = (ServletConfig) config;
37 
38     }
39 
40     // ----------------------------------------------------- Instance Variables
41 
42     /**
43      * 保存 該外觀類封裝的 原類
44      */
45     private ServletConfig config = null;
46 
47     // -------------------------------------------------- ServletConfig Methods
48 
49     /**
50      * 返回Servlet的名稱
51      */
52     public String getServletName() {
53         return config.getServletName();
54     }
55 
56     /**
57      * 獲取ServletContext 實例,這里 獲取的是 ServletContext的外觀類
58      */
59     public ServletContext getServletContext() {
60         ServletContext theContext = config.getServletContext();
61         if ((theContext != null) && (theContext instanceof ApplicationContext))
62             theContext = ((ApplicationContext) theContext).getFacade();
63         return (theContext);
64     }
65 
66     /**
67      * 返回指定的初始化參數 值
68      */
69     public String getInitParameter(String name) {
70         return config.getInitParameter(name);
71     }
72 
73     /**
74      * 返回 所有初始化參數的名字枚舉 若無初始化參數 則返回一個空的枚舉
75      */
76     public Enumeration getInitParameterNames() {
77         return config.getInitParameterNames();
78     }
79 
80 }

 對getServletContext方法會做一些處理 並不返回 ServletContext本身而是返回其外觀類,

為了更好的理解 下面的 SrandardWrapperValue功能 ,首先引入一個新的組件定義:

FilterDef類

org.apache.catalina.deploy.FilterDef類表示一個過濾器定義,過濾器通常都是 在Tomcat 部署描述文件中定義filter元素

  1 package org.apache.catalina.deploy;
  2 
  3 import java.util.HashMap;
  4 import java.util.Map;
  5 
  6 /**
  7  * 
  8  * <p>
  9  * <b>Title:FilterDef.java</b>
 10  * </p>
 11  * <p>
 12  * Copyright:ChenDong 2018
 13  * </p>
 14  * <p>
 15  * Company:僅學習時使用
 16  * </p>
 17  * <p>
 18  * 類功能描述:Web應用程序的過濾器定義的表示,如部署描述符中的{@code <filter>}元素所示。
 19  * </p>
 20  * 
 21  * <p>
 22  * 該類中的全部屬性 都對應 web部署描述中的{@code <filter>}元素 ,
 23  * </p>
 24  * <p>
 25  * {@code <filter>}元素中包含六個子元素
 26  * </p>
 27  * <p>
 28  * {@code icon }可選元素。該元素聲明了一個供IDE使用的圖片
 29  * </p>
 30  * <p>
 31  * {@code filter-name}必需元素,該元素給過濾器設置一個名稱。
 32  * </p>
 33  * <p>
 34  * {@code display-name}可選元素。設置一個給IDE使用的簡短名稱
 35  * </p>
 36  * <p>
 37  * {@code description} 可選元素。該元素給IDE提供過濾器的文件文檔內容
 38  * </p>
 39  * <p>
 40  * {@code filter-class} 必需元素。指定過濾器實現類的完全限定名。
 41  * </p>
 42  * <p>
 43  * {@code init-param} 可選元素,定義過濾器的初始化參數,一個過濾器可以包含多個初始化參數
 44  * </p>
 45  * 
 46  * 
 47  * @author 陳東
 48  * @date 2018年12月1日 下午9:23:03
 49  * @version 1.0
 50  */
 51 public final class FilterDef {
 52 
 53     // ------------------------------------------------------------- Properties
 54 
 55     /**
 56      * (可選元素)該過濾器的說明
 57      */
 58     private String description = null;
 59 
 60     public String getDescription() {
 61         return (this.description);
 62     }
 63 
 64     public void setDescription(String description) {
 65         this.description = description;
 66     }
 67 
 68     /**
 69      * 此過濾器的顯示名稱.
 70      */
 71     private String displayName = null;
 72 
 73     public String getDisplayName() {
 74         return (this.displayName);
 75     }
 76 
 77     public void setDisplayName(String displayName) {
 78         this.displayName = displayName;
 79     }
 80 
 81     /**
 82      * (必須元素)實現此過濾器的Java類的完全限定名
 83      */
 84     private String filterClass = null;
 85 
 86     public String getFilterClass() {
 87         return (this.filterClass);
 88     }
 89 
 90     public void setFilterClass(String filterClass) {
 91         this.filterClass = filterClass;
 92     }
 93 
 94     /**
 95      * (必需元素)這個過濾器的名稱,它必須在為特定web應用程序定義的過濾器中是唯一的.
 96      */
 97     private String filterName = null;
 98 
 99     public String getFilterName() {
100         return (this.filterName);
101     }
102 
103     public void setFilterName(String filterName) {
104         this.filterName = filterName;
105     }
106 
107     /**
108      * 
109      * (可選元素)與這個過濾器相關的大圖標
110      */
111     private String largeIcon = null;
112 
113     public String getLargeIcon() {
114         return (this.largeIcon);
115     }
116 
117     public void setLargeIcon(String largeIcon) {
118         this.largeIcon = largeIcon;
119     }
120 
121     /**
122      * 此過濾器的初始化參數集,由參數名鍵入。
123      */
124     private Map parameters = new HashMap();
125 
126     public Map getParameterMap() {
127 
128         return (this.parameters);
129 
130     }
131 
132     /**
133      * 與此過濾器相關聯的小圖標.
134      */
135     private String smallIcon = null;
136 
137     public String getSmallIcon() {
138         return (this.smallIcon);
139     }
140 
141     public void setSmallIcon(String smallIcon) {
142         this.smallIcon = smallIcon;
143     }
144 
145     // --------------------------------------------------------- Public Methods
146 
147     /**
148      * 向與此過濾器關聯的參數集添加初始化參數
149      *
150      * @param name
151      *            初始化參數的 name
152      * @param value
153      *            初始化參數的 value
154      */
155     public void addInitParameter(String name, String value) {
156 
157         parameters.put(name, value);
158 
159     }
160 
161     public String toString() {
162 
163         StringBuffer sb = new StringBuffer("FilterDef[");
164         sb.append("filterName=");
165         sb.append(this.filterName);
166         sb.append(", filterClass=");
167         sb.append(this.filterClass);
168         sb.append("]");
169         return (sb.toString());
170 
171     }
172 
173 }

然后這里在啰嗦幾句關於上面的過濾器定義類,FilterDef類中的每一個屬性表示在定義filter元素時生命的子元素,上面有寫哈,其中Map類型的變量 parameters存儲了初始化過濾器時所需要的所有參數,addInitparameter方法用於向parameters中添加新的name/value形式的參數名和對應的值。

FilterMap

org.apache.catalina.deploy.FilterMap類 是 我們常常用的 <filter-mapping>元素的定義組件 這個就不細說了 代碼上我都寫了注釋,有興趣的可以展開看下

  1 package org.apache.catalina.deploy;
  2 
  3 import org.apache.catalina.util.RequestUtil;
  4 
  5 /**
  6  * 
  7  * <p>
  8  * <b>Title:FilterMap.java</b>
  9  * </p>
 10  * <p>
 11  * Copyright:ChenDong 2018
 12  * </p>
 13  * <p>
 14  * Company:僅學習時使用
 15  * </p>
 16  * <p>
 17  * 類功能描述:Web應用程序的篩選器映射的表示,如部署描述符中的<code>&lt;filter-mapping&gt;</code>元素所示。
 18  * 每個篩選器映射必須包含篩選器名稱加上URL模式或servlet名稱。
 19  * </p>
 20  * <p>
 21  * 介紹一下 <code>&lt;filter-mapping&gt;</code>元素包含的幾個子元素
 22  * </p>
 23  * <p>
 24  * {@code filter-name } 必需, 該元素 必需與{@code <filter>}元素中聲明的匹配
 25  * </p>
 26  * <p>
 27  * {@code url-pattern} 必需,該元素 聲明了一個以/或者以*開始的匹配模式,這個模式指定了過濾器所應用的URL,但一個元素內只能配置一個
 28  * 若希望該過濾器應用於 多個 URL匹配模式 則應該提供多個 <code>&lt;filter-mapping&gt;</code>元素
 29  * </p>
 30  * <p>
 31  * {@code servlet-name} 這個是搭配使用的 但是如果使用 就必須有值,該元素給出的名稱必須匹配Servlet或JSP頁面的名稱,
 32  * </p>
 33  * 
 34  * @author 陳東
 35  * @date 2018年12月2日 下午3:03:29
 36  * @version 1.0
 37  */
 38 public final class FilterMap {
 39 
 40     // ------------------------------------------------------------- Properties
 41 
 42     /**
 43      * 當該映射與特定請求匹配時要執行的過濾器的名稱
 44      */
 45     private String filterName = null;
 46 
 47     public String getFilterName() {
 48         return (this.filterName);
 49     }
 50 
 51     public void setFilterName(String filterName) {
 52         this.filterName = filterName;
 53     }
 54 
 55     /**
 56      * 此映射匹配的servlet名稱。
 57      */
 58     private String servletName = null;
 59 
 60     public String getServletName() {
 61         return (this.servletName);
 62     }
 63 
 64     public void setServletName(String servletName) {
 65         this.servletName = servletName;
 66     }
 67 
 68     /**
 69      * 此映射匹配的URL模式。
 70      */
 71     private String urlPattern = null;
 72 
 73     public String getURLPattern() {
 74         return (this.urlPattern);
 75     }
 76 
 77     public void setURLPattern(String urlPattern) {
 78         this.urlPattern = RequestUtil.URLDecode(urlPattern);
 79     }
 80 
 81     // --------------------------------------------------------- Public Methods
 82 
 83     /**
 84      * Render a String representation of this object.
 85      */
 86     public String toString() {
 87 
 88         StringBuffer sb = new StringBuffer("FilterMap[");
 89         sb.append("filterName=");
 90         sb.append(this.filterName);
 91         if (servletName != null) {
 92             sb.append(", servletName=");
 93             sb.append(servletName);
 94         }
 95         if (urlPattern != null) {
 96             sb.append(", urlPattern=");
 97             sb.append(urlPattern);
 98         }
 99         sb.append("]");
100         return (sb.toString());
101 
102     }
103 
104 }
FilterMap

ApplicationFilterConfig類

org.apache.catalina.core.ApplicationFilterConfig類實現了 javax.servlet.FilterConfig接口,ApplicationFilterConfig類用於管理Web應用程序第一次啟動時,創建的所有的過濾器實例。

可以通過把一個 org.apcahe.catalina.Contex對象和 一個FilterDef對象傳遞給ApplicationFilterConfig的構造函數來創建一個ApplicationFilterConfig對象

 1     /**
 2      * 可以通過把一個{@link Context}對象 和一個 {@link FilterDef}對象傳遞給該構造函數,來創建一個新的
 3      * {@link ApplicationFilterConfig}實例
 4      *
 5      * @param context
 6      *            關聯的Context
 7      * @param filterDef
 8      *            要構造{@code ApplicationFilterConfig}的過濾器定義
 9      *
10      * @exception ClassCastException
11      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類 沒有實現
12      *                <code>javax.servlet.Filter</code> 接口,拋出該異常。
13      * 
14      * @exception ClassNotFoundException
15      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
16      *                沒有被加載器找到,拋出異常 。
17      * @exception IllegalAccessException
18      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
19      *                不能公開實例化,拋出異常 。
20      * @exception InstantiationException
21      *                如果實例化{@code Filter}對象時發生異常
22      * @exception ServletException
23      *                如果被過濾器的{@code init}方法拋出異常
24      */
25     public ApplicationFilterConfig(Context context, FilterDef filterDef) throws ClassCastException,
26             ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException {
27 
28         super();
29         this.context = context;
30         setFilterDef(filterDef);
31 
32     }

 其中的Context表示的是一個Web應用程序,FilterDef對象表示一個過濾器的定義,

該類中使用一個 org.apcache.catalina,Context類型的 私有變量  context 來接收 構造函數 傳入的 context對象

   /**
     * 與我們相關聯的Context對象(代表一個Web應用程序)
     */
    private Context context = null;

利用setFilterDef方法來設置 過濾器定義,且分配一個新的過濾器實例,下面看下方法定義

 1    /**
 2      * 
 3      * 設置我們配置的 過濾器定義對象,並且還會有實例化該過濾器的功能
 4      * 
 5      * @param filterDef
 6      *            新的過濾器定義
 7      *
 8      * @exception ClassCastException
 9      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類 沒有實現
10      *                <code>javax.servlet.Filter</code> 接口,拋出該異常。
11      * 
12      * @exception ClassNotFoundException
13      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
14      *                沒有被加載器找到,拋出異常 。
15      * @exception IllegalAccessException
16      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
17      *                不能公開實例化,拋出異常 。
18      * @exception InstantiationException
19      *                如果實例化{@code Filter}對象時發生異常
20      * @exception ServletException
21      *                如果被過濾器的{@code init}方法拋出異常
22      */
23     void setFilterDef(FilterDef filterDef) throws ClassCastException, ClassNotFoundException, IllegalAccessException,
24             InstantiationException, ServletException {
25 
26         this.filterDef = filterDef;
27         if (filterDef == null) {
28 
29             // 釋放任何先前分配的過濾器實例
30             if (this.filter != null)
31                 this.filter.destroy();
32             this.filter = null;
33 
34         } else {
35 
36             // 分配新的過濾器實例
37             Filter filter = getFilter();
38 
39         }
40 
41     }

ApplicationFilterConfig類的 getFilter方法會返回一個javax,servler.Filter對象,該方法負責載入並實例化一個過濾器

 1 /**
 2      * 返回已配置的應用程序過濾器器({@code javax.servlet.Filter}).
 3      *
 4      * @exception ClassCastException
 5      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類 沒有實現
 6      *                <code>javax.servlet.Filter</code> 接口,拋出該異常。
 7      * 
 8      * @exception ClassNotFoundException
 9      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
10      *                沒有被加載器找到,拋出異常 。
11      * @exception IllegalAccessException
12      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
13      *                不能公開實例化,拋出異常 。
14      * @exception InstantiationException
15      *                如果實例化{@code Filter}對象時發生異常
16      * @exception ServletException
17      *                如果被過濾器的{@code init}方法拋出異常
18      */
19     Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException,
20             InstantiationException, ServletException {
21 
22         // 返回現有的過濾器實例,如果有的話
23         if (this.filter != null)
24             return (this.filter);
25 
26         // 標識我們將使用的類加載器 土話 就是 把我們想定義的Filter 的類的完全限定名 取出來
27         String filterClass = filterDef.getFilterClass();
28         ClassLoader classLoader = null;
29         // 如果 我們要加載的Filter 是 Catalina包下的類 我們不需要使用 Context容器中的loader
30         // 直接使用當前的系統類加載器就行了
31         // 因為 loader的加載器 是有資源訪問限制的 這就不細說了
32         if (filterClass.startsWith("org.apache.catalina."))
33             classLoader = this.getClass().getClassLoader();
34         else
35             classLoader = context.getLoader().getClassLoader();
36 
37         // 搞什么飛機
38         ClassLoader oldCtxClassLoader = Thread.currentThread().getContextClassLoader();
39 
40         // 實例化此過濾器的一個新實例並返回它
41         Class clazz = classLoader.loadClass(filterClass);
42         this.filter = (Filter) clazz.newInstance();
43 
44         filter.init(this);
45         return (this.filter);
46 
47     }

下面直接把全部代碼貼出來

  1 package org.apache.catalina.core;
  2 
  3 import java.util.ArrayList;
  4 import java.util.Enumeration;
  5 import java.util.Map;
  6 import javax.servlet.Filter;
  7 import javax.servlet.FilterConfig;
  8 import javax.servlet.ServletContext;
  9 import javax.servlet.ServletException;
 10 import org.apache.catalina.Context;
 11 import org.apache.catalina.deploy.FilterDef;
 12 import org.apache.catalina.util.Enumerator;
 13 
 14 /**
 15  * 
 16  * <p>
 17  * <b>Title:ApplicationFilterConfig.java</b>
 18  * </p>
 19  * <p>
 20  * Copyright:ChenDong 2018
 21  * </p>
 22  * <p>
 23  * Company:僅學習時使用
 24  * </p>
 25  * <p>
 26  * 類功能描述:實現了<code>javax.servlet.FilterConfig</code>接口,
 27  * 用於管理Web應用程序第一次啟動時創建的所有的過濾器實例
 28  * </p>
 29  * 
 30  * @author 陳東
 31  * @date 2018年12月1日 下午10:05:41
 32  * @version 1.0
 33  */
 34 final class ApplicationFilterConfig implements FilterConfig {
 35 
 36     // ----------------------------------------------------------- Constructors
 37 
 38     /**
 39      * 可以通過把一個{@link Context}對象 和一個 {@link FilterDef}對象傳遞給該構造函數,來創建一個新的
 40      * {@link ApplicationFilterConfig}實例
 41      *
 42      * @param context
 43      *            關聯的Context(表示一個Web應用程序)
 44      * @param filterDef
 45      *            要構造{@code ApplicationFilterConfig}的過濾器定義
 46      *
 47      * @exception ClassCastException
 48      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類 沒有實現
 49      *                <code>javax.servlet.Filter</code> 接口,拋出該異常。
 50      * 
 51      * @exception ClassNotFoundException
 52      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
 53      *                沒有被加載器找到,拋出異常 。
 54      * @exception IllegalAccessException
 55      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
 56      *                不能公開實例化,拋出異常 。
 57      * @exception InstantiationException
 58      *                如果實例化{@code Filter}對象時發生異常
 59      * @exception ServletException
 60      *                如果被過濾器的{@code init}方法拋出異常
 61      */
 62     public ApplicationFilterConfig(Context context, FilterDef filterDef) throws ClassCastException,
 63             ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException {
 64 
 65         super();
 66         this.context = context;
 67         setFilterDef(filterDef);
 68 
 69     }
 70 
 71     // ----------------------------------------------------- Instance Variables
 72 
 73     /**
 74      * 與我們相關聯的Context對象(代表一個Web應用程序)
 75      */
 76     private Context context = null;
 77 
 78     /**
 79      * 我們配置的應用程序過濾器.
 80      */
 81     private Filter filter = null;
 82 
 83     /**
 84      * 我們配置的應用程序過濾器的<code>FilterDef</code>定義(Filter 是過濾器實例
 85      * 而<code>FilterDef</code>是定義過濾器的對象)
 86      */
 87     private FilterDef filterDef = null;
 88 
 89     // --------------------------------------------------- FilterConfig Methods
 90 
 91     /**
 92      * 返回我們配置的應用程序 過濾器的名字
 93      */
 94     public String getFilterName() {
 95 
 96         return (filterDef.getFilterName());
 97 
 98     }
 99 
100     /**
101      * 返回包含命名初始化參數的<code>String</code>類型的值,如果參數不存在,返回<code>null</code>。
102      *
103      * @param name
104      *            請求返回的初始化參數的名稱
105      */
106     public String getInitParameter(String name) {
107 
108         Map map = filterDef.getParameterMap();
109         if (map == null)
110             return (null);
111         else
112             return ((String) map.get(name));
113 
114     }
115 
116     /**
117      * 返回此篩選器的初始化參數名稱的<code>Enumeration</code>。
118      */
119     @SuppressWarnings("rawtypes")
120     public Enumeration getInitParameterNames() {
121 
122         Map map = filterDef.getParameterMap();
123         if (map == null)
124             return (new Enumerator(new ArrayList()));
125         else
126             return (new Enumerator(map.keySet()));
127 
128     }
129 
130     /**
131      * 
132      * 返回相關Web應用程序的{@link ServletContext}。
133      */
134     public ServletContext getServletContext() {
135 
136         return (this.context.getServletContext());
137 
138     }
139 
140     public String toString() {
141 
142         StringBuffer sb = new StringBuffer("ApplicationFilterConfig[");
143         sb.append("name=");
144         sb.append(filterDef.getFilterName());
145         sb.append(", filterClass=");
146         sb.append(filterDef.getFilterClass());
147         sb.append("]");
148         return (sb.toString());
149 
150     }
151 
152     // -------------------------------------------------------- Package Methods
153 
154     /**
155      * 返回已配置的應用程序過濾器器({@code javax.servlet.Filter}).
156      *
157      * @exception ClassCastException
158      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類 沒有實現
159      *                <code>javax.servlet.Filter</code> 接口,拋出該異常。
160      * 
161      * @exception ClassNotFoundException
162      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
163      *                沒有被加載器找到,拋出異常 。
164      * @exception IllegalAccessException
165      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
166      *                不能公開實例化,拋出異常 。
167      * @exception InstantiationException
168      *                如果實例化{@code Filter}對象時發生異常
169      * @exception ServletException
170      *                如果被過濾器的{@code init}方法拋出異常
171      */
172     Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException,
173             InstantiationException, ServletException {
174 
175         // 返回現有的過濾器實例,如果有的話
176         if (this.filter != null)
177             return (this.filter);
178 
179         // 標識我們將使用的類加載器 土話 就是 把我們想定義的Filter 的類的完全限定名 取出來
180         String filterClass = filterDef.getFilterClass();
181         ClassLoader classLoader = null;
182         // 如果 我們要加載的Filter 是 Catalina包下的類 我們不需要使用 Context容器中的loader
183         // 直接使用當前的系統類加載器就行了
184         // 因為 loader的加載器 是有資源訪問限制的 這就不細說了
185         if (filterClass.startsWith("org.apache.catalina."))
186             classLoader = this.getClass().getClassLoader();
187         else
188             classLoader = context.getLoader().getClassLoader();
189 
190         // 搞什么飛機
191         ClassLoader oldCtxClassLoader = Thread.currentThread().getContextClassLoader();
192 
193         // 實例化此過濾器的一個新實例並返回它
194         Class clazz = classLoader.loadClass(filterClass);
195         this.filter = (Filter) clazz.newInstance();
196 
197         filter.init(this);
198         return (this.filter);
199 
200     }
201 
202     /**
203      * 返回我們配置的 過濾器定義類 {@link FilterDef}
204      */
205     FilterDef getFilterDef() {
206 
207         return (this.filterDef);
208 
209     }
210 
211     /**
212      * 
213      * 如果存在,則釋放與此{@link FilterConfig}關聯的過濾器實例。
214      */
215     void release() {
216 
217         if (this.filter != null)
218             filter.destroy();
219         this.filter = null;
220 
221     }
222 
223     /**
224      * 
225      * 設置我們配置的 過濾器定義對象,並且還會有實例化該過濾器的功能
226      * 
227      * @param filterDef
228      *            新的過濾器定義
229      *
230      * @exception ClassCastException
231      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類 沒有實現
232      *                <code>javax.servlet.Filter</code> 接口,拋出該異常。
233      * 
234      * @exception ClassNotFoundException
235      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
236      *                沒有被加載器找到,拋出異常 。
237      * @exception IllegalAccessException
238      *                如果{@code FilterDef}中的 {@code filterClass}變量指定的類
239      *                不能公開實例化,拋出異常 。
240      * @exception InstantiationException
241      *                如果實例化{@code Filter}對象時發生異常
242      * @exception ServletException
243      *                如果被過濾器的{@code init}方法拋出異常
244      */
245     void setFilterDef(FilterDef filterDef) throws ClassCastException, ClassNotFoundException, IllegalAccessException,
246             InstantiationException, ServletException {
247 
248         this.filterDef = filterDef;
249         if (filterDef == null) {
250 
251             // 釋放任何先前分配的過濾器實例
252             if (this.filter != null)
253                 this.filter.destroy();
254             this.filter = null;
255 
256         } else {
257 
258             // 分配新的過濾器實例
259             Filter filter = getFilter();
260 
261         }
262 
263     }
264 
265     // -------------------------------------------------------- Private Methods
266 
267 }
ApplicationFilterConfig的類定義

 ApplicationFilterChain

過濾器鏈的定義

org.apache.catalina.core.ApplicationFilterChain類實現了 javax.servlert.FilterChain接口,StandardWrapperValue類的 invoke方法會創建ApplicationFliterChain類的一個實例,並調用其doFilter方法,ApplicationFilterChain類的doFilter方法會調用過濾器鏈中的第一個過濾器的doFilter方法,Filter接口的doFilter方法的簽名是

 1 void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException; 

這個熟悉 Container容器的管道的同學可能一看就看出來這個其實 和 管道的 ValveContext 對象的 invoke方法類似,目的都是把管道或者鏈中的所有對象都執行一遍,

那個可能有不熟悉的同學 這里就簡單的說下:ApplicationFilterChain類的doFilter方法 會將 ApplicationFilterChain自身作為第三個參數傳給過濾器的doFilter方法,在過濾器的doFilter中,可以顯示的通過調用FilterChain對象的doFilter方法來調用另一個過濾器,如果某個過濾器時過濾器鏈中的最后一個過濾器,則會調用被請求的Servlet類的service方法,如果過濾器沒有調用chain.doFilter方法,則不會調用后面的過濾器。

展示一下 Filter中的 doFilter方法的偽代碼

void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException{
/******** 想干啥干啥*********/
var3.doFilter(var1,var2);
}

然后同樣把全部代碼粘貼出來有興趣可以看下

  1 package org.apache.catalina.core;
  2 
  3 import java.io.IOException;
  4 import java.util.ArrayList;
  5 import java.util.Iterator;
  6 import java.security.PrivilegedActionException;
  7 import javax.servlet.Filter;
  8 import javax.servlet.FilterChain;
  9 import javax.servlet.Servlet;
 10 import javax.servlet.ServletException;
 11 import javax.servlet.ServletRequest;
 12 import javax.servlet.ServletResponse;
 13 import javax.servlet.http.HttpServletRequest;
 14 import javax.servlet.http.HttpServletResponse;
 15 import org.apache.catalina.InstanceEvent;
 16 import org.apache.catalina.util.InstanceSupport;
 17 import org.apache.catalina.util.StringManager;
 18 
 19 /**
 20  * 
 21  * <p>
 22  * <b>Title:ApplicationFilterChain.java</b>
 23  * </p>
 24  * <p>
 25  * Copyright:ChenDong 2018
 26  * </p>
 27  * <p>
 28  * Company:僅學習時使用
 29  * </p>
 30  * <p>
 31  * 類功能描述: 用於管理特定請求的一組過濾器執行鏈
 32  * 是<code>javax.servlet.FilterChain</code>的實現。當已定義的過濾器集全部執行完畢時,對
 33  * <code>doFilter()</code> 的下一次調用將執行servlet的<code>service()</code>方法。
 34  * </p>
 35  * 
 36  * @author 陳東
 37  * @date 2018年12月2日 上午10:47:54
 38  * @version 1.0
 39  */
 40 final class ApplicationFilterChain implements FilterChain {
 41 
 42     // ----------------------------------------------------------- Constructors
 43 
 44     /**
 45      * 構造一個沒有定義過濾器的新鏈實例
 46      */
 47     public ApplicationFilterChain() {
 48 
 49         super();
 50 
 51     }
 52 
 53     // ----------------------------------------------------- Instance Variables
 54 
 55     /**
 56      * 將在這個鏈上執行的一組過濾器。
 57      */
 58     private ArrayList filters = new ArrayList();
 59 
 60     /**
 61      * 
 62      * 用於保持過濾器鏈中當前位置的迭代器。這個迭代器被稱為第一次調用<code>doFilter()</code>
 63      */
 64     private Iterator iterator = null;
 65 
 66     /**
 67      * 要由這個鏈執行的servlet實例.
 68      */
 69     private Servlet servlet = null;
 70 
 71     /**
 72      * StringManger工具類.
 73      */
 74     private static final StringManager sm = StringManager.getManager(Constants.Package);
 75 
 76     /**
 77      * 
 78      * 與Wrapper關聯的InstanceSupport實例(用於發送“before filter”和“after filter”事件。
 79      */
 80     private InstanceSupport support = null;
 81 
 82     // ---------------------------------------------------- FilterChain Methods
 83 
 84     /**
 85      * 
 86      * 調用該鏈中的下一個過濾器,傳遞指定的請求和響應。如果在此鏈中不再有過濾器,則調用servlet本身的 <code>service()</code>
 87      * 方法
 88      * 
 89      * @param request
 90      *            我們正在處理的servlet請求
 91      * @param response
 92      *            我們正在創建的servlet響應
 93      *
 94      * @exception IOException
 95      *                如果發生輸入/輸出錯誤
 96      * @exception ServletException
 97      *                如果出現servlet異常
 98      */
 99     public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
100 
101         // 如果設置了安全管理器
102         if (System.getSecurityManager() != null) {
103             final ServletRequest req = request;
104             final ServletResponse res = response;
105             try {
106                 java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction() {
107                     public Object run() throws ServletException, IOException {
108                         internalDoFilter(req, res);
109                         return null;
110                     }
111                 });
112             } catch (PrivilegedActionException pe) {
113                 Exception e = pe.getException();
114                 if (e instanceof ServletException)
115                     throw (ServletException) e;
116                 else if (e instanceof IOException)
117                     throw (IOException) e;
118                 else if (e instanceof RuntimeException)
119                     throw (RuntimeException) e;
120                 else
121                     throw new ServletException(e.getMessage(), e);
122             }
123         } else {
124             internalDoFilter(request, response);
125         }
126     }
127 
128     /**
129      * 
130      * 
131      * <p>
132      * Title: internalDoFilter
133      * </p>
134      * 
135      * @date 2018年12月2日 上午10:54:27
136      * 
137      *       <p>
138      *       功能描述:真正干活的“doFilter”
139      *       </p>
140      * 
141      * @param request
142      * @param response
143      * @throws IOException
144      * @throws ServletException
145      */
146     private void internalDoFilter(ServletRequest request, ServletResponse response)
147             throws IOException, ServletException {
148 
149         // 第一次調用這個方法時構造 過濾器鏈的迭代器
150         if (this.iterator == null)
151             this.iterator = filters.iterator();
152 
153         // 如果鏈中有一個過濾器,則調用下一個過濾器
154         if (this.iterator.hasNext()) {
155             // 真正的過濾器 是由 ApplicationFilterConfig 類 的getFilter方法實例化的
156             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) iterator.next();
157             Filter filter = null;
158             try {
159                 filter = filterConfig.getFilter();
160                 // 觸發BEFORE_FILTER_EVENT 事件
161                 support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, response);
162                 // 真正執行 doFilter方法
163                 filter.doFilter(request, response, this);
164                 // 觸發AFTER_FILTER_EVENT事件
165                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response);
166             } catch (IOException e) {
167                 if (filter != null)
168                     support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e);
169                 throw e;
170             } catch (ServletException e) {
171                 if (filter != null)
172                     support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e);
173                 throw e;
174             } catch (RuntimeException e) {
175                 if (filter != null)
176                     support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e);
177                 throw e;
178             } catch (Throwable e) {
179                 if (filter != null)
180                     support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e);
181                 throw new ServletException(sm.getString("filterChain.filter"), e);
182             }
183             return;
184         }
185         // 到這里呢 說明聯中的過濾器已經執行完了 所以接下來要調用Servlet實例的 service方法了
186         try {
187             // 觸發 BEFORE_SERVICE_EVENT事件
188             support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT, servlet, request, response);
189             // 真正調用 Servlet的service方法
190             if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
191                 servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
192             } else {
193                 servlet.service(request, response);
194             }
195             // 觸發AFTER_SERVICE_EVENT事件
196             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response);
197         } catch (IOException e) {
198             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e);
199             throw e;
200         } catch (ServletException e) {
201             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e);
202             throw e;
203         } catch (RuntimeException e) {
204             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e);
205             throw e;
206         } catch (Throwable e) {
207             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e);
208             throw new ServletException(sm.getString("filterChain.servlet"), e);
209         }
210 
211     }
212 
213     // -------------------------------------------------------- Package Methods
214 
215     /**
216      * 將一個過濾器添加到將在這個鏈中執行的過濾器集合中.
217      *
218      * @param filterConfig
219      *            要執行的servlet的過濾器
220      */
221     void addFilter(ApplicationFilterConfig filterConfig) {
222 
223         this.filters.add(filterConfig);
224 
225     }
226 
227     /**
228      * 釋放對該鏈執行的過濾器和包裝器的引用。
229      */
230     void release() {
231 
232         this.filters.clear();
233         this.iterator = iterator;
234         this.servlet = null;
235 
236     }
237 
238     /**
239      * 置將在該鏈的末尾執行的servlet
240      *
241      * @param servlet
242      *            執行的servlet
243      */
244     void setServlet(Servlet servlet) {
245 
246         this.servlet = servlet;
247 
248     }
249 
250     /**
251      * 設置用於此過濾器鏈的事件通知的{@code InstanceSupport}對象。
252      * 
253      * @param support
254      *            InstanceSupport對象
255      */
256     void setSupport(InstanceSupport support) {
257 
258         this.support = support;
259 
260     }
261 
262 }
ApplicationFilterChain

 

StandardWrapperValue

StandardWrapperValue類 是StandardWrapper實例中的基礎閥,它主要完成兩個操作;

  1. 執行與該Servlet實例關聯的全部過濾器
  2. 調用Servlet實例的service方法

為了完成上述任務,在StandardWrapperVlaue類的 invoke方法中會執行以下幾個操作,

  1. 調用StandardWrapper實例的allocate方法獲取該StandardWrapper實例所表示的Servlet類實例。
  2. 調用私有方法 createFilterChain 創建過濾器鏈
  3. 調用過濾器鏈的 doFilter方法,其中包括調用Servlet實例的service方法,
  4. 釋放過濾器
  5. 調用Wrapper實例的deallocate方法;
  6. 過該Servlet類再也不會被使用到,則調用Wrapper實例的unload方法

 

展示一下 invoke方法的部分代碼

第一步 調用StandardWrapper類的allocate方法來分配一個Servlet實例

 1 // 分配servlet實例來處理此請求
 2         try {
 3             if (!unavailable) {
 4                 // 第一操作 調用StandardWrapper類的allocate方法 分配一個Servlet實例
 5                 servlet = wrapper.allocate();
 6             }
 7         } catch (ServletException e) {
 8             log(sm.getString("standardWrapper.allocateException", wrapper.getName()), e);
 9             throwable = e;
10             exception(request, response, e);
11             servlet = null;
12         } catch (Throwable e) {
13             log(sm.getString("standardWrapper.allocateException", wrapper.getName()), e);
14             throwable = e;
15             exception(request, response, e);
16             servlet = null;
17         }

 

第二步

創建過濾器鏈

// 第二步:調用私有方法 createFilterChain 來創建過濾器鏈
        ApplicationFilterChain filterChain = createFilterChain(request, servlet);

再展示下 createFilterChain的方法代碼

 1 /**
 2      * 
 3      * 構造並返回一個FilterChain實現,它將包裝指定servlet實例的執行。如果我們根本不執行一個過濾鏈,返回
 4      * <code>null</code>。
 5      * <p>
 6      * <strong>FIXME</strong> - 鏈實例!
 7      *
 8      * @param request
 9      *            我們正在處理的servlet請求
10      * @param servlet
11      *            要被包裝的Servlet實例
12      */
13     private ApplicationFilterChain createFilterChain(Request request, Servlet servlet) {
14 
15         // 如果沒有servlet執行,返回null
16         if (servlet == null)
17             return (null);
18 
19         // 創建並初始化過濾器鏈對象
20         ApplicationFilterChain filterChain = new ApplicationFilterChain();
21         filterChain.setServlet(servlet);
22         StandardWrapper wrapper = (StandardWrapper) getContainer();
23         filterChain.setSupport(wrapper.getInstanceSupport());
24 
25         // 獲取此Context的過濾器映射
26         StandardContext context = (StandardContext) wrapper.getParent();
27         // 這個過濾器映射集合怎么得來的 根據 web描述文件中的 <filter -mapping>
28         FilterMap filterMaps[] = context.findFilterMaps();
29 
30         // 如果沒有過濾器映射,我們就完成了
31         if ((filterMaps == null) || (filterMaps.length == 0))
32             return (filterChain);
33 
34         // 獲取匹配過濾器映射所需的信息
35         String requestPath = null;
36         if (request instanceof HttpRequest) {
37             HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
38             String contextPath = hreq.getContextPath();
39             if (contextPath == null)
40                 contextPath = "";
41             String requestURI = ((HttpRequest) request).getDecodedRequestURI();
42             if (requestURI.length() >= contextPath.length())
43                 requestPath = requestURI.substring(contextPath.length());
44         }
45         String servletName = wrapper.getName();
46 
47         int n = 0;
48 
49         // 將匹配到相關路徑映射的過濾器添加到該過濾器鏈
50         for (int i = 0; i < filterMaps.length; i++) {
51 
52             if (!matchFiltersURL(filterMaps[i], requestPath))
53                 continue;
54             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context
55                     .findFilterConfig(filterMaps[i].getFilterName());
56             if (filterConfig == null) {
57 
58                 continue;
59             }
60             filterChain.addFilter(filterConfig);
61             n++;
62         }
63 
64         // 添加與Servlet名稱匹配的過濾器
65         for (int i = 0; i < filterMaps.length; i++) {
66 
67             if (!matchFiltersServlet(filterMaps[i], servletName))
68                 continue;
69             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context
70                     .findFilterConfig(filterMaps[i].getFilterName());
71             if (filterConfig == null) {
72                 continue;
73             }
74             filterChain.addFilter(filterConfig);
75             n++;
76         }
77 
78         // 返回過濾器鏈
79         return (filterChain);
80 
81     }

第三步 執行過濾器的doFilter方法 ,在其鏈的doFilter方法最后 會調用 Servlet的service方法 可以參見上面的ApplicationFilterChain類

    if ((servlet != null) && (filterChain != null)) {
                // 第三步:調用過濾器的 doFilter方法 包括調用Servlet的service方法
                filterChain.doFilter(sreq, sres);
       }

 

第四步 釋放過濾器鏈

 1 try {
 2             if (filterChain != null)
 3                 // 第四步 釋放過濾器鏈
 4                 filterChain.release();
 5         } catch (Throwable e) {
 6             log(sm.getString("standardWrapper.releaseFilters", wrapper.getName()), e);
 7             if (throwable == null) {
 8                 throwable = e;
 9                 exception(request, response, e);
10             }
11         }

第五步 調用Wrapper的deallocate方法 將Servlet實例還給 StandardWrapper

 1 // 第五步 調用Wrapper實例的 deallocate方法
 2         try {
 3             if (servlet != null) {
 4 
 5                 wrapper.deallocate(servlet);
 6             }
 7         } catch (Throwable e) {
 8             log(sm.getString("standardWrapper.deallocateException", wrapper.getName()), e);
 9             if (throwable == null) {
10                 throwable = e;
11                 exception(request, response, e);
12             }
13         }

在給大家展示一下StandardWrapper類的dellocate方法

 1 /**
 2      * 
 3      * 將先前分配的servlet返回到可用實例池中。如果此servlet類不實現{@link SingleThreadModel}接口,
 4      * 則實際上不需要任何操作
 5      * 
 6      * @param servlet
 7      *            被返回來的Servlet實例
 8      *
 9      * @exception ServletException
10      *                如果發生了分配錯誤
11      */
12     public void deallocate(Servlet servlet) throws ServletException {
13 
14         // 如果此servlet類不實現{@link SingleThreadModel}接口,只需要把當前活動的Servlet實例計數減一 就可以了
15         if (!singleThreadModel) {
16             countAllocated--;
17             return;
18         }
19 
20         // 解鎖並釋放此實例
21         synchronized (instancePool) {
22             countAllocated--;
23             instancePool.push(servlet);
24             instancePool.notify();
25         }
26 
27     }

第六步 若該Servlet類在也不會被使用到,則調用Wrapper實例的unload方法

說這個方法之前 現引入一個StandardWrapper類的表示變量 

   /**
     * 這個servlet可用的日期和時間(自紀元以來以毫秒為單位),或者如果servlet可用,則為零。如果此值等於.{@code MAX_VALUE}
     * 則此servlet的不可用性被認為是永久性的。
     */
    private long available = 0L

這個變量是可以通過下面方法設置的

 1 /**
 2      * 設置該servlet的可用日期/時間,從該紀元開始,以毫秒為單位。如果此日期/時間在將來,
 3      * 則對該servlet的任何請求都將返回{@code SC_SERVICE_UNAVAILABLE}錯誤
 4      *
 5      * @param available
 6      *            新的可用日期/時間
 7      */
 8     public void setAvailable(long available) {
 9 
10         long oldAvailable = this.available;
11         if (available > System.currentTimeMillis())
12             this.available = available;
13         else
14             this.available = 0L;
15         support.firePropertyChange("available", new Long(oldAvailable), new Long(this.available));
16 
17     }

那么我們回過頭來看  第六步 如檢查到 上面的表示 為 MAX_VALUE,就會執行 wrapper 的unload方法

try {
            if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) {
                wrapper.unload();
            }
        } catch (Throwable e) {
            log(sm.getString("standardWrapper.unloadException", wrapper.getName()), e);
            if (throwable == null) {
                throwable = e;
                exception(request, response, e);
            }
        }

看下面 展示 unload方法

 1 /**
 2      * 在調用每個實例的<code>destroy()</code>方法后,卸載此servlet的所有初始化實例。例如,
 3      * 可以在關閉整個servlet引擎之前, 或者在從與Loader的存儲庫關聯的加載程序中重新加載所有類之前
 4      *
 5      * @exception ServletException
 6      *                if an exception is thrown by the destroy() method
 7      */
 8     public synchronized void unload() throws ServletException {
 9 
10         // 如果我們從來沒有加載過實例
11         if (!singleThreadModel && (instance == null))
12             return;
13         unloading = true;
14 
15         // 如果當前實例已分配,則暫時停留
16         // (可能超過一個,如果非STM)
17         if (countAllocated > 0) {
18             int nRetries = 0;
19             while (nRetries < 10) {
20                 if (nRetries == 0) {
21                     log("等待" + countAllocated + " 實例被解除分配 ");
22                 }
23                 try {
24                     Thread.sleep(50);
25                 } catch (InterruptedException e) {
26                     ;
27                 }
28                 nRetries++;
29             }
30         }
31 
32         // 獲取當前線程中的加載器
33         ClassLoader oldCtxClassLoader = Thread.currentThread().getContextClassLoader();
34         // 獲取當初實例化 Instance時 的加載器
35         ClassLoader classLoader = instance.getClass().getClassLoader();
36 
37         PrintStream out = System.out;
38         SystemLogHandler.startCapture();
39 
40         // 調用Servlet 的destory()方法
41         try {
42             instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_DESTROY_EVENT, instance);
43             Thread.currentThread().setContextClassLoader(classLoader);
44             instance.destroy();
45             instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT, instance);
46         } catch (Throwable t) {
47             instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT, instance, t);
48             instance = null;
49             instancePool = null;
50             nInstances = 0;
51             fireContainerEvent("unload", this);
52             unloading = false;
53             throw new ServletException(sm.getString("standardWrapper.destroyException", getName()), t);
54         } finally {
55             // 將上面存起來的線程加載器 重置回去
56             Thread.currentThread().setContextClassLoader(oldCtxClassLoader);
57             // Write captured output
58             String log = SystemLogHandler.stopCapture();
59             if (log != null && log.length() > 0) {
60                 if (getServletContext() != null) {
61                     getServletContext().log(log);
62                 } else {
63                     out.println(log);
64                 }
65             }
66         }
67 
68         // 撤銷被銷毀的實例
69         instance = null;
70 
71         if (singleThreadModel && (instancePool != null)) {
72             try {
73                 Thread.currentThread().setContextClassLoader(classLoader);
74                 // 這里是將 棧頂元素的元素取出進行銷毀(因為StandardWrapperValue的invoke方法會先將
75                 // Servlet換回來)
76                 // 這里 為什么要把 STM池中的Servlet實例都銷毀呢?大家不要忘記 哈 實質上N個STM
77                 // 實例全都是代表一個Servlet的 當初是為了提升性能才實例了多個,所以要全部銷毀
78                 while (!instancePool.isEmpty()) {
79                     ((Servlet) instancePool.pop()).destroy();
80                 }
81             } catch (Throwable t) {
82                 instancePool = null;
83                 nInstances = 0;
84                 unloading = false;
85                 fireContainerEvent("unload", this);
86                 throw new ServletException(sm.getString("standardWrapper.destroyException", getName()), t);
87             } finally {
88                 // restore the context ClassLoader
89                 Thread.currentThread().setContextClassLoader(oldCtxClassLoader);
90             }
91             instancePool = null;
92             nInstances = 0;
93         }
94 
95         unloading = false;
96         fireContainerEvent("unload", this);
97 
98     }

那么目前為止 關於 tomcat 的Servlet容器之一 標准實現的StandardWrapper 類的相關功能 就說完了 下一篇文章將會 展示 另一個 Servlet容器  Context的標准實現 StandardContext

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM