Tomcat中有四種類型的Servlet容器,分別是 Engine、Host、Context、Wrapper,每個Wrapper實例表示一個具體的Servlet定義,StandardWrapper就是Catalina中的Wrapper接口的標准實現.
方法調用序列:指的是每當服務器接收到Http請求時,服務器中調用的一系列方法,對於每個引入的HTTP請求,連接器都會調用與其關聯的Servlet容器的 invoke方法,然后,Servlet容器會調用其所有子容器的invoke方法,
具體的過程看下面:
- 連接器創建request和response對象;
 - 連接器調用StandardContext實例的invoke方法,
 - 接着StandardContext實例的invoke方法會調用其管道對象的額invoke方法,StandardContext對象的基礎閥是StandardContextValue類的實例,因此StandardContext的管道對象會調用其基礎閥的invoke方法,
 - StandardContextValue實例的invoke方法會獲取響應的Wrapper實例來處理HTTP請求,調用Wrapper實例的invoke方法
 - StandardWrapper類是Wrapper接口的標准實現,StandardWrapper對象會調用其管道對象的invoke方法。
 - StandardWrapper對象的基礎閥是StandardWrapperValue類的實例,因此會調用StandardWrapperValue的invoke方法,其invoke方法會調用Wrapper實例的allocate方法獲取servlet實例;
 - allocate方法會調用load方法載入相應的servlet類,若已經載入,咋無需重復載入,
 - load方法會調用servlet實例的init方法
 - 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.
意思大概:
- load-on-startup 元素標記容器是否應該在web應用程序啟動的時候就加載這個servlet,(實例化並調用其init()方法)。
 - 它的值必須是一個整數,表示servlet被加載的先后順序。
 - 如果該元素的值為負數或者沒有設置,則容器會當Servlet被請求時再加載。
 - 如果值為正整數或者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個方法;
- getServletContext()
 - getServletName()
 - getInitParameter
 - 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><filter-mapping></code>元素所示。 18 * 每個篩選器映射必須包含篩選器名稱加上URL模式或servlet名稱。 19 * </p> 20 * <p> 21 * 介紹一下 <code><filter-mapping></code>元素包含的幾個子元素 22 * </p> 23 * <p> 24 * {@code filter-name } 必需, 該元素 必需與{@code <filter>}元素中聲明的匹配 25 * </p> 26 * <p> 27 * {@code url-pattern} 必需,該元素 聲明了一個以/或者以*開始的匹配模式,這個模式指定了過濾器所應用的URL,但一個元素內只能配置一個 28 * 若希望該過濾器應用於 多個 URL匹配模式 則應該提供多個 <code><filter-mapping></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 }
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 }
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 }
StandardWrapperValue
StandardWrapperValue類 是StandardWrapper實例中的基礎閥,它主要完成兩個操作;
- 執行與該Servlet實例關聯的全部過濾器
 - 調用Servlet實例的service方法
 
為了完成上述任務,在StandardWrapperVlaue類的 invoke方法中會執行以下幾個操作,
- 調用StandardWrapper實例的allocate方法獲取該StandardWrapper實例所表示的Servlet類實例。
 - 調用私有方法 createFilterChain 創建過濾器鏈
 - 調用過濾器鏈的 doFilter方法,其中包括調用Servlet實例的service方法,
 - 釋放過濾器
 - 調用Wrapper實例的deallocate方法;
 - 過該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
