日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析

發(fā)布時(shí)間:2023/12/3 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
【0】README 0.0)本文部分文字描述轉(zhuǎn)自 “how tomcat works”,旨在學(xué)習(xí)?“tomcat(11)StandardWrapper源碼剖析” 的基礎(chǔ)知識(shí); 0.1)StandardWrapper 是 Catalina中對Wrapper接口的標(biāo)準(zhǔn)實(shí)現(xiàn);要知道,tomcat 中有4種類型的容器:Engine,Host,Context 和 Wrapper;(干貨——review ?tomcat 中有4種類型的容器:Engine,Host,Context 和 Wrapper
【1】方法調(diào)用序列 1)對于每個(gè)引入的http 請求,連接器都會(huì)調(diào)用與其關(guān)聯(lián)的servlet容器的 invoke() 方法。然后,servlet容器會(huì)調(diào)用其所有子容器的invoke() 方法; 2)下圖展示了連接器接收到http 請求后的方法調(diào)用的協(xié)作圖;
3)上圖的具體steps 如下: step1)連接器創(chuàng)建 request 和 response對象; step2)連接器調(diào)用StandardContext.invoke()方法; step3)StandardContext.invoke()方法調(diào)用其管道的invoke() 方法。StandardContext的管道對象的基礎(chǔ)閥是 StandardCoantextValve類的實(shí)例,因此, StandardContext 的管道會(huì)調(diào)用 StandardContextValve.invoke()方法; step4)StandardContextValve.invoke()方法 獲取相應(yīng)的Wrapper 實(shí)例處理 http請求,調(diào)用Wrapper實(shí)例的invoke()方法; step5)StandardWrapper類是Wrapper接口的標(biāo)準(zhǔn)實(shí)現(xiàn),StandardWrapper.invoke()方法 會(huì)調(diào)用其管道對象的invoke()方法; step6)StandardWrapper的管道對象中的基礎(chǔ)閥是 StandardWrapperValve 類的實(shí)例,因此,會(huì)調(diào)用StandardWrapperValve.invoke()方法,StandardWrapperValve.invoke()方法會(huì)調(diào)用Wrapper實(shí)例的 allocate() 方法獲取servlet實(shí)例; step7)allocate()方法調(diào)用load() 方法載入相應(yīng)的servlet類,若已經(jīng)載入,則無需重復(fù)載入; step8)load()方法調(diào)用servlet實(shí)例的init()方法; step9)StandardWrapperValve調(diào)用servlet.service()方法; // Call the filter chain for this request// NOTE: This also calls the servlet's service() methodtry { // org.apache.catalina.core.StandardWrapperValve.invoke()String jspFile = wrapper.getJspFile();if (jspFile != null)sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);elsesreq.removeAttribute(Globals.JSP_FILE_ATTR);if ((servlet != null) && (filterChain != null)) {filterChain.doFilter(sreq, sres); // highlight line. doFilter() calls servlet.service()}sreq.removeAttribute(Globals.JSP_FILE_ATTR);} Attention)StandardContext類的構(gòu)造函數(shù)會(huì)設(shè)置StandardContextValve類的一個(gè)實(shí)例作為其基礎(chǔ)閥;? public StandardContext() { // org.apache.catalina.core.StardardContextsuper();pipeline.setBasic(new StandardContextValve());namingResources.setContainer(this); } Attention)StandardWrapper類的構(gòu)造函數(shù)也會(huì)設(shè)置一個(gè) StandardWrapperValve實(shí)例作為其基礎(chǔ)閥: public StandardWrapper() { // org.apache.catalina.core.StardardWrappersuper();swValve=new StandardWrapperValve();pipeline.setBasic(swValve); } 4)依據(jù)上述(3)小節(jié)中的 “處理http 請求的方法調(diào)用協(xié)作圖”,本文按照慣例給出了具體的調(diào)用過程,如下:
4.1)本文第一張是借用了 “tomcat(10)安全性中章節(jié)【6.4】中Supplement-補(bǔ)充模塊”的第2張圖;(for spec info,please visit tomcat(10)安全性),這旨在說明從HttpConnector -> StandardContext.invoke() -> StandardPipeline.invoke()的調(diào)用過程;
4.2)本文接著上面的調(diào)用過程繼續(xù)分析,調(diào)用過程如下圖;旨在說明? StandardPipeline.invoke() -> StandardContextValve.invoke() -> StandardWrapper.invoke() -> StandardPipeline.invoke() -> StandardWrapperValve.invoke() -> ApplicationFilterChain().doFilter()? ->?ApplicationFilterChain().internalDoFilter() -> HttpServlet(ModernServlet).service() -> ModernServlet->doGet() 的調(diào)用過程.(Bingo)

對上述協(xié)作圖和詳細(xì)調(diào)用過程圖的分析(Analysis): A0)要知道Tomcat中有4種容器:Engine,Host,Context 和 Wrapper;(干貨——本文一直強(qiáng)調(diào)這一點(diǎn),理解容器的層次結(jié)構(gòu)對于理解tomcat非常重要) A1)StandardContext 和 StandardWrapper 都是容器:他們都繼承自 ContainerBase,只不過StandardWrapper是StandardContext的子容器,而StandardWrapper是最小的容器,即它沒有子容器; A2)下面分別看StandardWrapper,StandardContext的構(gòu)造函數(shù)?和 ContainerBase 的變量定義; public final class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper {public StandardWrapper() {super();swValve=new StandardWrapperValve();pipeline.setBasic(swValve);} } public class StandardContext extends ContainerBase implements Context {public StandardContext() { super();pipeline.setBasic(new StandardContextValve());namingResources.setContainer(this);} } public abstract class ContainerBase implements Container, Lifecycle, Pipeline {protected Pipeline pipeline = new StandardPipeline(this); // highlight line.protected HashMap children = new HashMap();protected int debug = 0; protected LifecycleSupport lifecycle = new LifecycleSupport(this); protected ArrayList listeners = new ArrayList(); protected Loader loader = null; protected Logger logger = null; protected Manager manager = null; protected Cluster cluster = null; protected Mapper mapper = null; protected HashMap mappers = new HashMap(); protected String mapperClass = null; protected String name = null; protected Container parent = null; protected ClassLoader parentClassLoader = null; protected Pipeline pipeline = new StandardPipeline(this); protected Realm realm = null; protected DirContext resources = null; protected static StringManager sm = StringManager.getManager(Constants.Package); protected boolean started = false; protected PropertyChangeSupport support = new PropertyChangeSupport(this); } A3)可以看到 父容器ContainerBase定義了管道StandardPipeline,而子容器StandardContext 設(shè)置StandardContextValve為基礎(chǔ)閥;而最小的容器StandardWrapper設(shè)置StandardWrapperValve為基礎(chǔ)閥; A4)也即 StandardContext 和?StandardWrapper 共用同一個(gè)管道,分別設(shè)置不同的基礎(chǔ)閥;(當(dāng)然,可以分別設(shè)置非基礎(chǔ)閥,非基礎(chǔ)閥在基礎(chǔ)閥被調(diào)用之前調(diào)用);
【2】SingleThreadModel(已經(jīng)被棄用了) 1)intro:servlet類可以實(shí)現(xiàn) javax.servlet.SingleThreadModel 接口,這樣的servlet類也稱為 SingleThreadModel(STM)servlet類。根據(jù)servlet規(guī)范,實(shí)現(xiàn)此接口的目的是保證 servlet實(shí)例一次只處理一個(gè)請求; Attention)若 servlet類實(shí)現(xiàn) SingleThreadModel接口,則可以保證絕不會(huì)有兩個(gè)線程同時(shí)執(zhí)行該servlet.service()方法。這一點(diǎn)由 servlet容器通過控制對單一 servlet實(shí)例的同步訪問實(shí)現(xiàn),或者維護(hù)一個(gè) servlet實(shí)例池,然后將每個(gè)新請求分派給一個(gè)空閑的servlet實(shí)例。該接口并不能防止servlet訪問共享資源造成的同步問題,例如訪問類的靜態(tài)變量或訪問servlet作用域之外的類; (干貨——有很多程序員哥哥沒有讀懂這段話,想當(dāng)然的認(rèn)為,實(shí)現(xiàn)了該接口的servlet就是線程安全的。這種想法是錯(cuò)誤的,請?jiān)俣纫槐樯厦娴囊膬?nèi)容(原文作者說的,哈哈))
2)事實(shí)上,實(shí)現(xiàn)了 SingleThreadModel 接口的servlet類只能保證在同一時(shí)刻,只有一個(gè)線程在執(zhí)行該 servlet實(shí)例的service()方法。但,為了提高執(zhí)行 性能,servlet容器會(huì)創(chuàng)建多個(gè)STM servlet實(shí)例。也就是說,STM servlet.service()方法 會(huì)在多個(gè)STM?servlet實(shí)例中并發(fā)執(zhí)行。如果servlet實(shí)例需要靜態(tài)類變量或類外的某些資源的話,就有可能引起同步問題; Atttention)在servlet 2.4中,SingleThreadModel接口已經(jīng)被棄用了,因?yàn)樗鼤?huì)使 servlet程序員誤以為該接口的servlet類就是多線程安全的;
【3】StandardWrapper 1)intro to?StandardWrapper:其主要任務(wù)是 載入它所代表的servlet類,并進(jìn)行實(shí)例化; 2)StandardWrapper并不調(diào)用servlet的service方法,該任務(wù)由 StandardWrapperValve對象(StandardWrapper實(shí)例的管道對象中的基礎(chǔ)閥)完成; 3)StandardWrapperValve對象通過調(diào)用allocate()方法從 StandardWrapper實(shí)例中獲取servlet實(shí)例,在獲得servlet實(shí)例后,StandardWrapperValve實(shí)例就會(huì)調(diào)用servlet實(shí)例的service()方法; 【3.1】分配servlet實(shí)例? 1)分配servlet實(shí)例是由 StandardWrapper.allocate()方法來完成的(allocate方法返回請求的servelt實(shí)例); 2)allocate()方法分為兩部分(parts): p1)第一部分:?allocate()首先檢查 instance是否為null,若是, 則allocate()方法調(diào)用 loadServlet()方法載入相關(guān)的servlet類,然后 整型變量countAllocated加1,返回instance的值; p2)第二部分: p2.1)若StandardWrapper表示的servlet是一個(gè)STM servlet類,則allocate()會(huì)試圖從對象池中返回一個(gè)servlet實(shí)例。變量 instancePool 是一個(gè) java.util.Stack類型的棧,其中保存了所有的STM servlet實(shí)例: private Stack instancePool = null; p2.2)只要STM servlet實(shí)例數(shù)不超過指定的最大值,allocate()方法會(huì)返回一個(gè) STM servlet實(shí)例。整型變量maxInstances 保存了在棧中存儲(chǔ)的 STM servlet實(shí)例的最大值,default value = 20; private int maxInstances = 20; p2.3)而 nInstances 保存了當(dāng)前 STM servlet實(shí)例的數(shù)量(初始為0);

3)源碼如下

public Servlet allocate() throws ServletException { //org.apache.catalina.core.StandardWrapper.allocate() // part 1 begins.if (debug >= 1) log("Allocating an instance");// If we are currently unloading this servlet, throw an exceptionif (unloading)throw new ServletException(sm.getString("standardWrapper.unloading", getName()));// If not SingleThreadedModel, return the same instance every timeif (!singleThreadModel) {// Load and initialize our instance if necessaryif (instance == null) {synchronized (this) {if (instance == null) {try {instance = loadServlet();} catch (ServletException e) {throw e;} catch (Throwable e) {throw new ServletException(sm.getString("standardWrapper.allocate"), e);}}}}if (!singleThreadModel) {if (debug >= 2)log(" Returning non-STM instance");countAllocated++;return (instance);}} // part1 ends. // part2 starts.synchronized (instancePool) {while (countAllocated >= nInstances) {// Allocate a new instance if possible, or else waitif (nInstances < maxInstances) {try {instancePool.push(loadServlet());nInstances++;} catch (ServletException e) {throw e;} catch (Throwable e) {throw new ServletException(sm.getString("standardWrapper.allocate"), e);}} else {try {instancePool.wait();} catch (InterruptedException e) {;}}}if (debug >= 2)log(" Returning allocated STM instance");countAllocated++;return (Servlet) instancePool.pop();}}// part2 ends.

【3.2】載入servlet類 1)StandardWrapper類實(shí)現(xiàn)了Wrapper接口的 load() 方法,load() 方法調(diào)用loadServlet()方法載入某個(gè)servlet類,并調(diào)用其 init() 方法,此時(shí)要傳入一個(gè) javax.servlet.ServletConfig實(shí)例作為參數(shù); 2)loadServlet() 方法是如何工作的 public synchronized void load() throws ServletException { // org.apache.catalina.core.StandardWrapper.load()instance = loadServlet(); }public synchronized Servlet loadServlet() throws ServletException { // org.apache.catalina.core.StandardWrapper.loadServlet()// Nothing to do if we already have an instance or an instance poolif (!singleThreadModel && (instance != null))return instance;PrintStream out = System.out;if (swallowOutput) {SystemLogHandler.startCapture();}Servlet servlet = null;try {// If this "servlet" is really a JSP file, get the right class.// HOLD YOUR NOSE - this is a kludge that avoids having to do special// case Catalina-specific code in Jasper - it also requires that the// servlet path be replaced by the <jsp-file> element content in// order to be completely effectiveString actualClass = servletClass;if ((actualClass == null) && (jspFile != null)) {Wrapper jspWrapper = (Wrapper)((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);if (jspWrapper != null)actualClass = jspWrapper.getServletClass();}// Complain if no servlet class has been specifiedif (actualClass == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.notClass", getName()));} // Acquire an instance of the class loader to be usedLoader loader = getLoader();if (loader == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingLoader", getName()));} ClassLoader classLoader = loader.getClassLoader(); // Special case class loader for a container provided servletif (isContainerProvidedServlet(actualClass)) {classLoader = this.getClass().getClassLoader();log(sm.getString("standardWrapper.containerServlet", getName()));} // Load the specified servlet class from the appropriate class loaderClass classClass = null;try {if (classLoader != null) {classClass = classLoader.loadClass(actualClass);} else {classClass = Class.forName(actualClass);}} catch (ClassNotFoundException e) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass),e);}if (classClass == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass));} // Instantiate and initialize an instance of the servlet class itselftry {servlet = (Servlet) classClass.newInstance();} catch (ClassCastException e) {unavailable(null);// Restore the context ClassLoaderthrow new ServletException(sm.getString("standardWrapper.notServlet", actualClass), e);} catch (Throwable e) {unavailable(null);// Restore the context ClassLoaderthrow new ServletException(sm.getString("standardWrapper.instantiate", actualClass), e);} // Check if loading the servlet in this web application should be// allowedif (!isServletAllowed(servlet)) {throw new SecurityException(sm.getString("standardWrapper.privilegedServlet",actualClass));} // Special handling for ContainerServlet instancesif ((servlet instanceof ContainerServlet) &&isContainerProvidedServlet(actualClass)) {((ContainerServlet) servlet).setWrapper(this);} // Call the initialization method of this servlettry {instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,servlet);servlet.init(facade);// Invoke jspInit on JSP pagesif ((loadOnStartup >= 0) && (jspFile != null)) {// Invoking jspInitHttpRequestBase req = new HttpRequestBase();HttpResponseBase res = new HttpResponseBase();req.setServletPath(jspFile);req.setQueryString("jsp_precompile=true");servlet.service(req, res);}instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet);} catch (UnavailableException f) {instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet, f);unavailable(f);throw f;} catch (ServletException f) {instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet, f);// If the servlet wanted to be unavailable it would have// said so, so do not call unavailable(null).throw f;} catch (Throwable f) {instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet, f);// If the servlet wanted to be unavailable it would have// said so, so do not call unavailable(null).throw new ServletException(sm.getString("standardWrapper.initException", getName()), f);} // Register our newly initialized instancesingleThreadModel = servlet instanceof SingleThreadModel;if (singleThreadModel) {if (instancePool == null)instancePool = new Stack();}fireContainerEvent("load", this);} finally {if (swallowOutput) {String log = SystemLogHandler.stopCapture();if (log != null && log.length() > 0) {if (getServletContext() != null) {getServletContext().log(log);} else {out.println(log);}}}}return servlet;}
step1)檢查當(dāng)前的StandardWrapper類是否表示的是一個(gè) STM servlet類,若不是,且變量instance不為null(表示以前已經(jīng)載入過這個(gè)servlet),它就直接返回該實(shí)例; // Nothing to do if we already have an instance or an instance poolif (!singleThreadModel && (instance != null))return instance; step2)獲得 System.out 和 System.err 的輸出,便于它使用 javax.servlet.ServletConfig.log() 方法記錄日志消息: PrintStream out = System.out;if (swallowOutput) {SystemLogHandler.startCapture();} step3)定義類型為javax.servlet.Servlet 名為servlet 的變量,其表示已載入的servlet實(shí)例,會(huì)由 loadServlet()方法返回; Servlet servlet = null; step4)由于Catalina是一個(gè)JSP容器,故loadServlet()方法必須檢查請求的servlet是不是一個(gè)jsp 頁面。若是,則loadServlet() 方法需要獲取代表該jsp 頁面的實(shí)際servlet類; String actualClass = servletClass;if ((actualClass == null) && (jspFile != null)) {Wrapper jspWrapper = (Wrapper)((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);if (jspWrapper != null)actualClass = jspWrapper.getServletClass(); } // public static final String JSP_SERVLET_NAME = "jsp";<span style="font-family: SimSun; line-height: 1.5; background-color: inherit;">? ? ? ? ? ?</span> step5)如果找不到該jsp 頁面的servlet類,則會(huì)使用變量 servletClass(actualClass)的值。若沒有調(diào)用StandardWrapper.serServletClass() 方法設(shè)置servletClass的值,則會(huì)拋出異常,并停止執(zhí)行后續(xù)方法; // Complain if no servlet class has been specifiedif (actualClass == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.notClass", getName()));}<span style="font-family: SimSun; background-color: rgb(255, 255, 255);">? ? ? ? ? ?</span> step6)這時(shí),要載入的servlet類名已經(jīng)解析完了,loadServlet()方法會(huì)獲取載入器 Loader loader = getLoader();public Loader getLoader() { // org.apache.catalina.core.ContainerBase.getLoader();if (loader != null)return (loader);if (parent != null)return (parent.getLoader());return (null);} step7)若找到載入器(loader),則loadServlet()方法調(diào)用getClassLoader()方法獲取一個(gè)ClassLoader; ClassLoader classLoader = loader.getClassLoader(); step8)Catalina提供了一些用于訪問servlet容器內(nèi)部數(shù)據(jù)的專用servlet類。如果某個(gè)servlet類是這種專用的servlet,即若isContainerProvidedServlet()方法返回true,則變量 classLoader被賦值為另一種ClassLoader實(shí)例,如此一來,這個(gè)servlet實(shí)例就可以訪問Catalina的內(nèi)部數(shù)據(jù)了; // Special case class loader for a container provided servletif (isContainerProvidedServlet(actualClass)) {classLoader = this.getClass().getClassLoader();log(sm.getString("standardWrapper.containerServlet", getName()));} step9)準(zhǔn)備好類載入器和準(zhǔn)備載入的servlet類名后,loadServlet()方法就可以載入servlet類了; // Load the specified servlet class from the appropriate class loaderClass classClass = null;try {if (classLoader != null) {classClass = classLoader.loadClass(actualClass);} else {classClass = Class.forName(actualClass);}} catch (ClassNotFoundException e) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass),e);} if (classClass == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass));} step10)實(shí)例化該servlet ? ? ? // Instantiate and initialize an instance of the servlet class itselftry {servlet = (Servlet) classClass.newInstance();} catch (ClassCastException e) {unavailable(null);// Restore the context ClassLoaderthrow new ServletException(sm.getString("standardWrapper.notServlet", actualClass), e);} catch (Throwable e) {unavailable(null);// Restore the context ClassLoaderthrow new ServletException(sm.getString("standardWrapper.instantiate", actualClass), e);}
step11)在loadServlet()方法實(shí)例化這個(gè)servlet之前,它會(huì)調(diào)用 isServletAllowed()方法檢查該servlet 類是否允許載入:? ? ? // Check if loading the servlet in this web application should be// allowedif (!isServletAllowed(servlet)) {throw new SecurityException(sm.getString("standardWrapper.privilegedServlet",actualClass));}
step12)若通過了安全檢查,它還會(huì)繼續(xù)檢查該servlet類是否是一個(gè) ContainerServlet類型的servlet(實(shí)現(xiàn)了 org.apache.catalina.ContainerServlet接口的 servlet可以訪問Catalina的內(nèi)部功能)。若該servlet類是一個(gè) ContainerServlet,loadServlet()方法會(huì)調(diào)用 ContainerServlet.setWrapper(),傳入StandardWrapper實(shí)例;? // Special handling for ContainerServlet instancesif ((servlet instanceof ContainerServlet) &&isContainerProvidedServlet(actualClass)) {((ContainerServlet) servlet).setWrapper(this);} step13)觸發(fā)BEFORE_INIT_EVENT事件,調(diào)用servlet實(shí)例的 init()方法(init()方法傳入了javax.servlet.ServletConfig外觀對象):? // Call the initialization method of this servlettry {instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,servlet);servlet.init(facade); // highlight line. step14)若變量 loadOnStartup 大于0, 且被請求的servlet類實(shí)際上是一個(gè)jsp 頁面,則servlet實(shí)例的service()方法;? if ((loadOnStartup >= 0) && (jspFile != null)) {// Invoking jspInitHttpRequestBase req = new HttpRequestBase();HttpResponseBase res = new HttpResponseBase();req.setServletPath(jspFile);req.setQueryString("jsp_precompile=true");servlet.service(req, res); // highlight line.}instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet); step15)觸發(fā)AFTER_INIT_EVENT事件 instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, step16)若StandardWrapper對象表示的servlet類是一個(gè)STM servlet,則將該servlet實(shí)例添加到servlet實(shí)例池中。因此會(huì)判斷 instancePool 是否為null,若是,則要給他賦值一個(gè)Stack 對象;? // Register our newly initialized instancesingleThreadModel = servlet instanceof SingleThreadModel;if (singleThreadModel) {if (instancePool == null)instancePool = new Stack(); // highlight line. } step17)在finally代碼塊中,loadServlet()方法停止捕獲System.out 和 System.err 對象,記錄在載入 ServletContext.log()方法的過程中產(chǎn)生的日志消息; finally {if (swallowOutput) {String log = SystemLogHandler.stopCapture();if (log != null && log.length() > 0) {if (getServletContext() != null) {getServletContext().log(log); // highlight line.} else {out.println(log);}}}} public ServletContext getServletContext() { org.apache.catalina.core.StandardWrapper.getServletContext()if (parent == null)return (null);else if (!(parent instanceof Context))return (null);elsereturn (((Context) parent).getServletContext());} step18)最后返回已載入的servlet實(shí)例; return servlet; 【3.3】ServletConfig對象 1)intro:在上述step13)中提到了 servlet.init(facade),而facade 是 javax.servlet.ServletConfig對象的一個(gè)外觀變量; 2)StandardWrapper對象是如何獲取 servletConfig 對象的?答案就在 StandardWrapper中,該類不僅實(shí)現(xiàn)了 Wrapper接口,還實(shí)現(xiàn)了 javax.servlet.ServletConfig 接口; public final class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper { // org.apache.catalina.core.StandardWrapper // ...... } public interface ServletConfig { // javax.servlet.ServletConfig public String getServletName();public ServletContext getServletContext(); public String getInitParameter(String name);public Enumeration getInitParameterNames(); } 3)javax.servlet.ServletConfig 接口有4個(gè)方法:getServletContext() , getServletName(), ?getInitParameter(), ?getInitParameterNames()方法;下面對這4個(gè)方法進(jìn)行說明; method1)getServletConfig()方法: public ServletContext getServletContext() { // org.apache.catalina.core.StandardWrapper.getServletContext()if (parent == null)return (null);else if (!(parent instanceof Context))return (null);elsereturn (((Context) parent).getServletContext());}/*** Return the servlet context for which this Context is a facade.*/public ServletContext getServletContext() { // org.apache.catalina.core.StandardContext.getServletContext()if (context == null)context = new ApplicationContext(getBasePath(), this);return (context);} Attention)正如以上代碼所展示的那樣,無法單獨(dú)使用一個(gè)Wrapper實(shí)例來表示一個(gè) servlet 類的定義。Wrapper 實(shí)例必須駐留在某個(gè) Context 容器中,這樣,當(dāng)調(diào)用其父容器的getServletConfig()方法時(shí),才能返回ServletContext類的一個(gè)實(shí)例; method2)getServletName()方法:該方法返回 servlet類的名字,該方法的簽名如下: ?public String getServletName() { // org.apache.catalina.core.StandardWrapper.getServletName()return (getName());} public String getName() { // org.apache.catalina.core.ContainerBase.getName(). // 因?yàn)?public final class StandardWrapper extends ContainerBasereturn (name);} method3)getInitParameter()方法:該方法返回指定初始參數(shù)的值 public String getInitParameter(String name) { // org.apache.catalina.core.StandardWrapper.getInitParameter()return (findInitParameter(name));} public String findInitParameter(String name) { // org.apache.catalina.core.StandardWrapper.findInitParameter()synchronized (parameters) {return ((String) parameters.get(name));}} 對getInitParameter()方法的分析(Analysis): A1)在StandardWrapper類中,初始化參數(shù) parameters 存儲(chǔ)在一個(gè) HashMap類型中; private HashMap parameters = new HashMap(); A2)通過addInitParameter()方法,傳入?yún)?shù)的名字 和 對應(yīng)的值 來填充變量 parameters 的值: public void addInitParameter(String name, String value) { // org.apache.catalina.core.StandardWrapper.addInitParameter().synchronized (parameters) {parameters.put(name, value);}fireContainerEvent("addInitParameter", name); // highlight line.} public void fireContainerEvent(String type, Object data) {// org.apache.catalina.core.ContainerBase.fireContainerEvent().if (listeners.size() < 1)return;ContainerEvent event = new ContainerEvent(this, type, data);ContainerListener list[] = new ContainerListener[0];synchronized (listeners) {list = (ContainerListener[]) listeners.toArray(list);}for (int i = 0; i < list.length; i++)((ContainerListener) list[i]).containerEvent(event);} A3)StandardWrapper.getInitParameter()方法的實(shí)現(xiàn)如下:? public String getInitParameter(String name) {return (findInitParameter(name));} A4)findInitParameter()方法接收一個(gè)指定的初始化參數(shù)名的字符串變量,調(diào)用HashMap 變量 parameters的get()方法獲取初始化參數(shù)的值; public String findInitParameter(String name) { // org.apache.catalina.core.StandardWrapper.findInitParameter()synchronized (parameters) {return ((String) parameters.get(name)); // highlight line.}} method4)getInitParameterNames()方法: 該方法返回所有初始化參數(shù)的名字的集合,實(shí)際上是 java.util.Enumeration的實(shí)例;? public Enumeration getInitParameterNames() {synchronized (parameters) {return (new Enumerator(parameters.keySet()));}} 【3.4】servlet容器的父子關(guān)系 1)intro to StandardWrapper:Wrapper實(shí)例代表一個(gè)servlet實(shí)例,是最低級(jí)的容器,故Wrapper不能再有子容器,不應(yīng)該調(diào)用addChild()方法添加子容器,否則拋出 java.lang.IllegalStateException 異常;(干貨review——Wrapper實(shí)例代表一個(gè)servlet實(shí)例,是最低級(jí)的容器,故Wrapper不能再有子容器 2)org.apache.catalina.core.StandardWrapper.addChild()方法實(shí)現(xiàn)如下: public void addChild(Container child) {throw new IllegalStateException (sm.getString("standardWrapper.notChild"));} Attention)Wrapper容器的父容器只能是 Context 容器;若我們在設(shè)置父容器的時(shí)候,傳入了非Context容器,則拋出 java.lang.IllegalArgumentException 異常; public void setParent(Container container) { // org.apache.catalina.core.StandardWrapper.setParent().if ((container != null) && !(container instanceof Context)) throw new IllegalArgumentException(sm.getString("standardWrapper.notContext"));if (container instanceof StandardContext) {swallowOutput = ((StandardContext)container).getSwallowOutput();}super.setParent(container); // highlight line.} public void setParent(Container container) { // org.apache.catalina.core.ContainerBase.setParent().Container oldParent = this.parent;this.parent = container;support.firePropertyChange("parent", oldParent, this.parent);}

【4】 StandardWrapperFacade類?(干貨——應(yīng)用了設(shè)計(jì)模式中的外觀模式) 1)problem+solution: 1.1)problem:StandardWrapper實(shí)例會(huì)調(diào)用它所載入的servlet類的實(shí)例的init()方法。init()方法需要一個(gè)javax.servlet.ServletConfig 實(shí)例,而StandardWrapper了本身也實(shí)現(xiàn)了 javax.servlet.ServletConfig 接口,所以,理論上 StandardWrapper需要將其中大部分公共方法對servlet程序員隱藏起來; 1.2)solution:為了實(shí)現(xiàn)這個(gè)目的,StandardWrapper類將自身實(shí)例包裝成 StandardWrapperFacade類的一個(gè)實(shí)例;
2)StandardWrapper類創(chuàng)建StandardWrapperFacade對象,并將自身作為參數(shù)傳入StandardWrapperFacade的構(gòu)造器; private StandardWrapperFacade facade = new StandardWrapperFacade(this); // defined in StandardWrapper.java 3)StandardWrapperFacade的構(gòu)造函數(shù); public StandardWrapperFacade(StandardWrapper config) {super();this.config = (ServletConfig) config; // private ServletConfig config = null;} 4)因此當(dāng)創(chuàng)建StandardWrapper對象調(diào)用servlet實(shí)例的 init()方法時(shí),它會(huì)傳入StandardWrapperFacade類的一個(gè)實(shí)例。這樣,在servlet實(shí)例內(nèi)調(diào)用 ServletConfig.getServletName(),ServletConfig.getInitParameter(), getInitParameterNames() ,getServletContext()方法會(huì)直接傳遞給 StandardWrapper類的相應(yīng)方法; 5)org.apache.catalina.core.StandardWrapperFacade 的定義如下: public final class StandardWrapperFacade implements ServletConfig {public StandardWrapperFacade(StandardWrapper config) {super();this.config = (ServletConfig) config;}public String getServletName() {return config.getServletName();}public ServletContext getServletContext() {ServletContext theContext = config.getServletContext();if ((theContext != null) &&(theContext instanceof ApplicationContext))theContext = ((ApplicationContext) theContext).getFacade();return (theContext);}public String getInitParameter(String name) {return config.getInitParameter(name);}public Enumeration getInitParameterNames() {return config.getInitParameterNames();} }

【5】StandardWrapperValve類 1)StandardWrapperValve類是 StandardWrapper實(shí)例中的基礎(chǔ)閥,要完成兩個(gè)操作(Operations): public StandardWrapper() { // StandardWrapper的構(gòu)造函數(shù);super();swValve=new StandardWrapperValve();pipeline.setBasic(swValve);} O1)執(zhí)行與該servlet實(shí)例關(guān)聯(lián)的全部過濾器;(干貨——這里引入了過濾器) O2)調(diào)用servlet實(shí)例的service()方法; 2)完成上述任務(wù)后,在 StandardWrapperValve.invoke()方法實(shí)現(xiàn)中會(huì)執(zhí)行以下操作(Operations): O1)調(diào)用StandardWrapper.allocate()方法獲取該StandardWrapper實(shí)例所表示的 servlet實(shí)例; public void invoke(Request request, Response response,ValveContext valveContext)throws IOException, ServletException {long t1=System.currentTimeMillis();requestCount++;// Initialize local variables we may needboolean unavailable = false;Throwable throwable = null;StandardWrapper wrapper = (StandardWrapper) getContainer();ServletRequest sreq = request.getRequest();ServletResponse sres = response.getResponse();Servlet servlet = null;HttpServletRequest hreq = null;if (sreq instanceof HttpServletRequest)hreq = (HttpServletRequest) sreq;HttpServletResponse hres = null;if (sres instanceof HttpServletResponse)hres = (HttpServletResponse) sres;// Check for the application being marked unavailableif (!((Context) wrapper.getParent()).getAvailable()) {hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,sm.getString("standardContext.isUnavailable"));unavailable = true;}// Check for the servlet being marked unavailableif (!unavailable && wrapper.isUnavailable()) {log(sm.getString("standardWrapper.isUnavailable",wrapper.getName()));if (hres == null) {; // NOTE - Not much we can do generically} else {long available = wrapper.getAvailable();if ((available > 0L) && (available < Long.MAX_VALUE))hres.setDateHeader("Retry-After", available);hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,sm.getString("standardWrapper.isUnavailable",wrapper.getName()));}unavailable = true;}// Allocate a servlet instance to process this requesttry {if (!unavailable) {servlet = wrapper.allocate(); // highlight line.} // ...... O2)調(diào)用私有方法 createFilterChain(),創(chuàng)建過濾器鏈; // Create the filter chain for this request ApplicationFilterChain filterChain = createFilterChain(request, servlet); // for create FilterChain方法,本章節(jié)末尾; private ApplicationFilterChain createFilterChain(Request request, Servlet servlet) { if (servlet == null)return (null);ApplicationFilterChain filterChain = new ApplicationFilterChain(); filterChain.setServlet(servlet);StandardWrapper wrapper = (StandardWrapper) getContainer();filterChain.setSupport(wrapper.getInstanceSupport());// Acquire the filter mappings for this ContextStandardContext context = (StandardContext) wrapper.getParent();FilterMap filterMaps[] = context.findFilterMaps();// If there are no filter mappings, we are doneif ((filterMaps == null) || (filterMaps.length == 0))return (filterChain);// Acquire the information we will need to match filter mappingsString requestPath = null;if (request instanceof HttpRequest) {HttpServletRequest hreq =(HttpServletRequest) request.getRequest();String contextPath = hreq.getContextPath();if (contextPath == null)contextPath = "";String requestURI = ((HttpRequest) request).getDecodedRequestURI();if (requestURI.length() >= contextPath.length())requestPath = requestURI.substring(contextPath.length());}String servletName = wrapper.getName();int n = 0;// Add the relevant path-mapped filters to this filter chainfor (int i = 0; i < filterMaps.length; i++) {if (!matchFiltersURL(filterMaps[i], requestPath))continue;ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue;}filterChain.addFilter(filterConfig);n++;}// Add filters that match on servlet name secondfor (int i = 0; i < filterMaps.length; i++) {if (!matchFiltersServlet(filterMaps[i], servletName))continue;ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue;}filterChain.addFilter(filterConfig);n++;}return (filterChain);} O3)調(diào)用過濾器鏈的 doFilter()方法,其中包括調(diào)用servlet實(shí)例的service()方法; try {String jspFile = wrapper.getJspFile();if (jspFile != null)sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);elsesreq.removeAttribute(Globals.JSP_FILE_ATTR);if ((servlet != null) && (filterChain != null)) {filterChain.doFilter(sreq, sres); // hightlight line.}sreq.removeAttribute(Globals.JSP_FILE_ATTR); // ......public void doFilter(ServletRequest request, ServletResponse response) //org.apache.catlina.core.ApplicationFilterChain.doFileter()throws IOException, ServletException {if( System.getSecurityManager() != null ) {final ServletRequest req = request;final ServletResponse res = response;try {java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction(){public Object run() throws ServletException, IOException {internalDoFilter(req,res); // highlight line. internalDoFilter() 參見文末.return null;}});} catch( PrivilegedActionException pe) {Exception e = pe.getException();if (e instanceof ServletException)throw (ServletException) e;else if (e instanceof IOException)throw (IOException) e;else if (e instanceof RuntimeException)throw (RuntimeException) e;elsethrow new ServletException(e.getMessage(), e);}} else {internalDoFilter(request,response);}} private void internalDoFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException { //org.apache.catalina.core.ApplicationFilterChain.internalDoFilter().// Construct an iterator the first time this method is calledif (this.iterator == null)this.iterator = filters.iterator();// Call the next filter if there is oneif (this.iterator.hasNext()) {ApplicationFilterConfig filterConfig =(ApplicationFilterConfig) iterator.next();Filter filter = null;try {filter = filterConfig.getFilter();support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,filter, request, response);filter.doFilter(request, response, this);support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,filter, request, response);} //......return;}// We fell off the end of the chain -- call the servlet instancetry {support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,servlet, request, response);if ((request instanceof HttpServletRequest) &&(response instanceof HttpServletResponse)) { servlet.service((HttpServletRequest) request, (HttpServletResponse) response); // 這不就是你夢寐以求的service()方法嗎?哈哈。} else {servlet.service(request, response); // and this highlight line.}support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,servlet, request, response);} //......} O4)釋放過濾器鏈; try {if (filterChain != null)filterChain.release(); // highlight line.} catch (Throwable e) {log(sm.getString("standardWrapper.releaseFilters",wrapper.getName()), e);if (throwable == null) {throwable = e;exception(request, response, e);}} void release() { //org.apache.catalina.core.ApplicationFilterChain.release()this.filters.clear();this.iterator = iterator;this.servlet = null;} O5)調(diào)用Wrapper實(shí)例的 deallocate()方法; // Deallocate the allocated servlet instancetry {if (servlet != null) {wrapper.deallocate(servlet); // highlight line.}} catch (Throwable e) {log(sm.getString("standardWrapper.deallocateException",wrapper.getName()), e);if (throwable == null) {throwable = e;exception(request, response, e);}}public void deallocate(Servlet servlet) throws ServletException { //org.apache.catalina.core.StandardWrapper.deallocate()// If not SingleThreadModel, no action is requiredif (!singleThreadModel) {countAllocated--;return;}synchronized (instancePool) {countAllocated--;instancePool.push(servlet);instancePool.notify();}} O6)若該servlet類再也不會(huì)被使用到,調(diào)用Wrapper實(shí)例的unload()方法; // If this servlet has been marked permanently unavailable,// unload it and release this instancetry {if ((servlet != null) &&(wrapper.getAvailable() == Long.MAX_VALUE)) {wrapper.unload(); // highlight line.}} // ......long t2=System.currentTimeMillis();long time=t2-t1;processingTime+=time;if( time > maxTime ) maxTime=time;} Attention)以上調(diào)用過程中,最重要的是對 createFilterChain()方法和過濾器鏈的 doFilter()方法的調(diào)用。createFilterChain()方法創(chuàng)建一個(gè) ApplicationFilterChain實(shí)例,并將所有需要應(yīng)用到該Wrapper實(shí)例所代表的servlet實(shí)例的過濾器添加到其中; private ApplicationFilterChain createFilterChain(Request request,Servlet servlet) {if (servlet == null)return (null);ApplicationFilterChain filterChain =new ApplicationFilterChain();filterChain.setServlet(servlet);StandardWrapper wrapper = (StandardWrapper) getContainer();filterChain.setSupport(wrapper.getInstanceSupport());StandardContext context = (StandardContext) wrapper.getParent();FilterMap filterMaps[] = context.findFilterMaps();if ((filterMaps == null) || (filterMaps.length == 0))return (filterChain);// Acquire the information we will need to match filter mappingsString requestPath = null;if (request instanceof HttpRequest) {HttpServletRequest hreq =(HttpServletRequest) request.getRequest();String contextPath = hreq.getContextPath();if (contextPath == null)contextPath = "";String requestURI = ((HttpRequest) request).getDecodedRequestURI();if (requestURI.length() >= contextPath.length())requestPath = requestURI.substring(contextPath.length());}String servletName = wrapper.getName();int n = 0; // Add the relevant path-mapped filters to this filter chainfor (int i = 0; i < filterMaps.length; i++) {if (!matchFiltersURL(filterMaps[i], requestPath)) continue;ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue; }filterChain.addFilter(filterConfig); n++;}// Add filters that match on servlet name secondfor (int i = 0; i < filterMaps.length; i++) {if (!matchFiltersServlet(filterMaps[i], servletName)) continue;ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue;}filterChain.addFilter(filterConfig);n++;}return (filterChain); } public synchronized void unload() throws ServletException {if (!singleThreadModel && (instance == null))return;unloading = true;if (countAllocated > 0) {int nRetries = 0;while (nRetries < 10) {if (nRetries == 0) {log("Waiting for " + countAllocated +" instance(s) to be deallocated");}try {Thread.sleep(50);} catch (InterruptedException e) {;}nRetries++;}}ClassLoader oldCtxClassLoader =Thread.currentThread().getContextClassLoader();ClassLoader classLoader = instance.getClass().getClassLoader();PrintStream out = System.out;if (swallowOutput) {SystemLogHandler.startCapture();}try {instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_DESTROY_EVENT, instance);Thread.currentThread().setContextClassLoader(classLoader);instance.destroy();instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT, instance);} catch (Throwable t) {instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT, instance, t);instance = null;instancePool = null;nInstances = 0;fireContainerEvent("unload", this);unloading = false;throw new ServletException(sm.getString("standardWrapper.destroyException", getName()),t);} finally {Thread.currentThread().setContextClassLoader(oldCtxClassLoader);if (swallowOutput) {String log = SystemLogHandler.stopCapture();if (log != null && log.length() > 0) {if (getServletContext() != null) {getServletContext().log(log);} else {out.println(log);}}}}instance = null;if (singleThreadModel && (instancePool != null)) {try {Thread.currentThread().setContextClassLoader(classLoader);while (!instancePool.isEmpty()) {((Servlet) instancePool.pop()).destroy();}} catch (Throwable t) {instancePool = null;nInstances = 0;unloading = false;fireContainerEvent("unload", this);throw new ServletException(sm.getString("standardWrapper.destroyException",getName()), t);} finally {Thread.currentThread().setContextClassLoader(oldCtxClassLoader);}instancePool = null;nInstances = 0;}singleThreadModel = false;unloading = false;fireContainerEvent("unload", this); }
【6】 FilterDef類(org.apache.catalina.deploy.FilterDef) 1)intro:FilterDef 是一個(gè)過濾器的定義; 2)FilterDef類中的每個(gè)屬性表示在定義filter元素時(shí)聲明的子元素。其中Map 類型的變量parameters 存儲(chǔ)了初始化過濾器時(shí)所需要的所有參數(shù)。addInitParameter()方法用于向parameters 中添加新的 name/value 形式的參數(shù)名和對應(yīng)的值; 3)其定義源碼如下: public final class FilterDef { // org.apache.catalina.deploy.FilterDef private String description = null;public String getDescription() {return (this.description);}public void setDescription(String description) {this.description = description;} private String displayName = null;public String getDisplayName() {return (this.displayName);}public void setDisplayName(String displayName) {this.displayName = displayName;}private String filterClass = null;public String getFilterClass() {return (this.filterClass);}public void setFilterClass(String filterClass) {this.filterClass = filterClass;} private String filterName = null;public String getFilterName() {return (this.filterName);}public void setFilterName(String filterName) {this.filterName = filterName;} private String largeIcon = null;public String getLargeIcon() {return (this.largeIcon);}public void setLargeIcon(String largeIcon) {this.largeIcon = largeIcon;} private Map parameters = new HashMap();public Map getParameterMap() {return (this.parameters);}private String smallIcon = null;public String getSmallIcon() {return (this.smallIcon);}public void setSmallIcon(String smallIcon) {this.smallIcon = smallIcon;} public void addInitParameter(String name, String value) {parameters.put(name, value);}public String toString() {StringBuffer sb = new StringBuffer("FilterDef[");sb.append("filterName=");sb.append(this.filterName);sb.append(", filterClass=");sb.append(this.filterClass);sb.append("]");return (sb.toString());} }

【7】ApplicationFilterConfig類(org.apache.catalina.core.ApplicationFilterConfig-應(yīng)用過濾器配置類) 1)intro:ApplicationFilterConfig類實(shí)現(xiàn)了 javax.servlet.FilterConfig接口,該類用于管理web 應(yīng)用程序第1次啟動(dòng)時(shí)創(chuàng)建的所有過濾器實(shí)例; 2)類簽名:final class ApplicationFilterConfig implements FilterConfig? 3)可以通過把一個(gè) org.apache.catalina.Context對象和 一個(gè) FilterDef對象傳遞給 ApplicationFilterConfig類的構(gòu)造函數(shù)來創(chuàng)建一個(gè) ApplicationFilterConfig對象:? public ApplicationFilterConfig(Context context, FilterDef filterDef)throws ClassCastException, ClassNotFoundException,IllegalAccessException, InstantiationException,ServletException {super();this.context = context;setFilterDef(filterDef);} 對以上代碼的分析(Analysis): A1)Context對象表示一個(gè)web 應(yīng)用程序; A2)FilterDef對象表示一個(gè)過濾器的定義; 4)ApplicationFilterConfig.getFilter()方法:會(huì)返回一個(gè) javax.servlet.Filter對象,該方法負(fù)責(zé)載入并實(shí)例化一個(gè)過濾器類;? public String getFilterName() { // org.apache.catalina.core.ApplicationFilterConfig.getFilterName().return (filterDef.getFilterName()); }
【8】ApplicationFilterChain類(org.apache.catalina.core.ApplicationFilterChain) 1)intro:?ApplicationFilterChain類實(shí)現(xiàn)了 javax.servlet.FilterChain接口,StandardWrapperValve.invoke() 方法會(huì)創(chuàng)建 ApplicationFilterChain類的一個(gè)實(shí)例,并調(diào)用其 doFilter()方法; 2)Filter接口的doFilter()方法的簽名如下: public interface Filter { // javax.servlet.Filterpublic void init(FilterConfig filterConfig) throws ServletException; public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException; public void destroy(); }3)ApplicationFilterChain.doFilter()方法會(huì)將?ApplicationFilterChain 類自身作為第3個(gè)參數(shù)傳遞給過濾器的 doFilter()方法; public void doFilter(ServletRequest request, ServletResponse response) //org.apache.catalina.ApplicationFilterChain.doFileter().throws IOException, ServletException {if( System.getSecurityManager() != null ) {final ServletRequest req = request;final ServletResponse res = response;try {java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction(){public Object run() throws ServletException, IOException {internalDoFilter(req,res);return null;}});} catch( PrivilegedActionException pe) {Exception e = pe.getException();if (e instanceof ServletException)throw (ServletException) e;else if (e instanceof IOException)throw (IOException) e;else if (e instanceof RuntimeException)throw (RuntimeException) e;elsethrow new ServletException(e.getMessage(), e);}} else {internalDoFilter(request,response);}} 4)在Filter.doFilter()方法中, 可以通過顯示地調(diào)用 FileterChain.doFilter()方法來調(diào)用另一個(gè)過濾器。
對以上代碼的分析(Analysis): A1)正如你所看到的,在doFilter()方法的最后一行會(huì)調(diào)用FilterChain.doFilter()方法; A2)如果某個(gè)過濾器時(shí)過濾器鏈中的最后一個(gè)過濾器,則會(huì)調(diào)用被請求的 servlet類的 service()方法。如果過濾器沒有調(diào)用chain.doFilter()方法,則不會(huì)調(diào)用后面的過濾器;
【9】應(yīng)用程序 0)servlet文件目錄
1)程序源代碼 public final class Bootstrap {public static void main(String[] args) {//invoke: http://localhost:8080/Modern or http://localhost:8080/PrimitiveSystem.setProperty("catalina.base", System.getProperty("user.dir"));Connector connector = new HttpConnector();Wrapper wrapper1 = new StandardWrapper();wrapper1.setName("Primitive");wrapper1.setServletClass("servlet.PrimitiveServlet"); // attention for servlet class,要與你的servlet目錄相對應(yīng);Wrapper wrapper2 = new StandardWrapper();wrapper2.setName("Modern");wrapper2.setServletClass("servlet.ModernServlet"); // attention for servlet class,要與你的servlet目錄相對應(yīng);Context context = new StandardContext();// StandardContext's start method adds a default mappercontext.setPath("/myApp");context.setDocBase("myApp");LifecycleListener listener = new SimpleContextConfig();((Lifecycle) context).addLifecycleListener(listener);context.addChild(wrapper1);context.addChild(wrapper2);// for simplicity, we don't add a valve, but you can add// valves to context or wrapper just as you did in Chapter 6Loader loader = new WebappLoader();context.setLoader(loader);// context.addServletMapping(pattern, name);context.addServletMapping("/Primitive", "Primitive");context.addServletMapping("/Modern", "Modern");// add ContextConfig. This listener is important because it configures// StandardContext (sets configured to true), otherwise StandardContext// won't startconnector.setContainer(context);try {connector.initialize();((Lifecycle) connector).start();((Lifecycle) context).start();// make the application wait until we press a key.System.in.read();((Lifecycle) context).stop();}catch (Exception e) {e.printStackTrace();}} } 2)打印結(jié)果 E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-common. jar;lib/commons-collections.jar;lib/naming-resources.jar;lib/;lib/catalina.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomca t.chapter11.startup.Bootstrap HttpConnector Opening server socket on all host IP addresses HttpConnector[8080] Starting background thread WebappLoader[/myApp]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\_\myApp StandardManager[/myApp]: Seeding random number generator class java.security.SecureRandom StandardManager[/myApp]: Seeding of random number generator has been completed StandardManager[/myApp]: IOException while loading persisted sessions: java.io.EOFException // // 這是從文件中加載 session對象到內(nèi)存,由于沒有相關(guān)文件,所以加載失敗,拋出異常,但這不會(huì)影響我們訪問servlet,大家不要驚慌; java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at com.tomcat.chapter11.startup.Bootstrap.main(Bootstrap.java:55) StandardManager[/myApp]: Exception loading sessions from persistent storage java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at com.tomcat.chapter11.startup.Bootstrap.main(Bootstrap.java:55) ModernServlet -- init


總結(jié)

以上是生活随笔為你收集整理的tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。