本文接下來介紹并分析servlet容器,servlet容器是用來處理請求servlet資源,并為web客戶端填充response對象的模塊。
servlet容器是org.apache.catalina.Container接口的實例,在tomcat中,有四種類型的容器,分別為Engine、Host 、Context和Wrapper。
Engine. 代表整個容器的servlet引擎。
Host.代表一個擁有一定數量Context的虛擬主機。
Context.代表一個Web項目.一個Context包含一個或多個Wrapper。
Wrapper.代表單獨的一個servlet。
這些容器構成一個自頂向下的等級結構,高等級的容器可以具有多個直接下屬等級的容器實例(子容器),這有點類似于composite模式,不過還是有差別的
org.apache.catalina.Container接口聲明如下
// 添加 public void addChild(Container child); // 刪除 public void removeChild(Container child); // 查找 public Container findChild(String name); // 查找全部 public Container[] findChildren();
上面方法均為操作子容器的相關方法
容器可以包含一些支持的組件,諸如載入器、記錄器、管理器、領域和資源等,我們可以通過編輯server.xml文件來決定使用哪種容器。
下面我們來分析servlet容器是怎么執行任務的,這里就要提到servlet容器的管道模型,管道包含了該servlet容器將要調用的任務,而閥則表示一個具體的執行任務;在servlet容器的管道中,有一個基礎閥,也可以添加任意數量的額外閥,閥的數量通常是指額外添加的閥的數量,不包括基礎閥
這里就好像servlet編程中的過濾器模型,管道好比過濾器鏈,而閥則是具體的過濾器;基礎閥總是最后一個執行的。
這里涉及幾個相關的接口,包括Pipeline、Valve、ValveContext 和Contained
Pipeline接口聲明如下
public interface Pipeline { public Valve getBasic(); public void setBasic(Valve valve); public void addValve(Valve valve); public Valve[] getValves(); public void invoke(Request request, Response response) throws IOException, ServletException; public void removeValve(Valve valve); }
Valve接口聲明如下
public interface Valve {
public String getInfo(); public void invoke(Request request, Response response, ValveContext context) throws IOException, ServletException; }
ValveContext接口聲明
public interface ValveContext { public String getInfo(); public void invokeNext(Request request, Response response) throws IOException, ServletException; }
Contained接口聲明
public interface Contained { public Container getContainer(); public void setContainer(Container container); }
閥可以選擇是否實現該接口,設置閥與一個servlet容器相關連
下面我們來學習Wrapper容器,Wrapper容器表示一個獨立的servlet定義,負責管理其基礎servlet類的生命周期,它繼承了Container接口,另外添加了額外方法聲明。其中比較重要的方法聲明是load()方法和allocate()方法,均與載入及初始化servlet類相關(供基礎閥調用,基礎閥持有Wrapper容器實例引用)
下面來分析一個簡單的Wrapper類,該類實現了org.apache.catalina.Wrapper接口和org.apache.catalina.Pipeline接口
public class SimpleWrapper implements Wrapper, Pipeline { // the servlet instance private Servlet instance = null ; private String servletClass; private Loader loader; private SimplePipeline pipeline = new SimplePipeline( this ); protected Container parent = null ; public SimpleWrapper() { pipeline.setBasic( new SimpleWrapperValve()); } public synchronized void addValve(Valve valve) { pipeline.addValve(valve); } public Servlet allocate() throws ServletException { // Load and initialize our instance if necessary if (instance== null ) { try { instance = loadServlet(); } catch (ServletException e) { throw e; } catch (Throwable e) { throw new ServletException("Cannot allocate a servlet instance" , e); } } return instance; } private Servlet loadServlet() throws ServletException { if (instance!= null ) return instance; Servlet servlet = null ; String actualClass = servletClass; if (actualClass == null ) { throw new ServletException("servlet class has not been specified" ); } Loader loader = getLoader(); // Acquire an instance of the class loader to be used if (loader== null ) { throw new ServletException("No loader." ); } ClassLoader classLoader = loader.getClassLoader(); // Load the specified servlet class from the appropriate class loader Class classClass = null ; try { if (classLoader!= null ) { classClass = classLoader.loadClass(actualClass); } } catch (ClassNotFoundException e) { throw new ServletException("Servlet class not found" ); } // Instantiate and initialize an instance of the servlet class itself try { servlet = (Servlet) classClass.newInstance(); } catch (Throwable e) { throw new ServletException("Failed to instantiate servlet" ); } // Call the initialization method of this servlet try { servlet.init( null ); } catch (Throwable f) { throw new ServletException("Failed initialize servlet." ); } return servlet; } public Loader getLoader() { if (loader != null ) return (loader); if (parent != null ) return (parent.getLoader()); return ( null ); } public void invoke(Request request, Response response) throws IOException, ServletException { pipeline.invoke(request, response); } public void load() throws ServletException { instance = loadServlet(); }
// method implementations of Pipeline public Valve getBasic() { return pipeline.getBasic(); } public void setBasic(Valve valve) { pipeline.setBasic(valve); } public Valve[] getValves() { return pipeline.getValves(); } public void removeValve(Valve valve) { pipeline.removeValve(valve); } }
上面的SimpleWrapper類由于實現了org.apache.catalina.Pipeline接口接口,同時與該接口相關的實現方法都是調用引用的成員變量SimplePipeline pipeline = new SimplePipeline(this)的對應方法,因此我們可以理解為SimpleWrapper類為SimplePipeline的包裝類
在它的invoke()方法里面調用了成員變量的SimplePipeline pipeline = new SimplePipeline(this)的invoke()方法,這里構造函數傳入SimpleWrapper實例本身,可以猜想是為了獲取其載入器及具體的servlet實現類(注:該方法為Container接口與Pipeline接口都具有的方法聲明,因此SimpleWrapper類只要一個實現),下面我們繼續分析SimplePipeline相關實現
public class SimplePipeline implements Pipeline { public SimplePipeline(Container container) { setContainer(container); } // The basic Valve (if any) associated with this Pipeline. protected Valve basic = null ; // The Container with which this Pipeline is associated. protected Container container = null ; // the array of Valves protected Valve valves[] = new Valve[0 ]; public void setContainer(Container container) { this .container = container; } public Valve getBasic() { return basic; } public void setBasic(Valve valve) { this .basic = valve; ((Contained) valve).setContainer(container); } public void addValve(Valve valve) { if (valve instanceof Contained) ((Contained) valve).setContainer( this .container); synchronized (valves) { Valve results[] = new Valve[valves.length +1 ]; System.arraycopy(valves, 0, results, 0 , valves.length); results[valves.length] = valve; valves = results; } } public Valve[] getValves() { return valves; } public void invoke(Request request, Response response) throws IOException, ServletException { // Invoke the first Valve in this pipeline for this request ( new SimplePipelineValveContext()).invokeNext(request, response); } public void removeValve(Valve valve) { } // this class is copied from org.apache.catalina.core.StandardPipeline class's // StandardPipelineValveContext inner class. protected class SimplePipelineValveContext implements ValveContext { protected int stage = 0 ; public String getInfo() { return null ; } public void invokeNext(Request request, Response response) throws IOException, ServletException { int subscript = stage; stage = stage + 1 ; // Invoke the requested Valve for the current request thread if (subscript < valves.length) { valves[subscript].invoke(request, response, this ); } else if ((subscript == valves.length) && (basic != null )) { basic.invoke(request, response, this ); } else { throw new ServletException("No valve" ); } } } // end of inner class }
invoke()方法里面調用內部類SimplePipelineValveContext(實現了ValveContext接口),遍歷執行各個閥的invoke()方法
wrapper容器執行的基本流程如上所述,下面我們來進一步分析相關輔助類及實現類等
在應用初始化servlet容器時,我們需要為其指定一個載入器,下面是一個簡單的載入器,實現了Loader接口
public class SimpleLoader implements Loader { public static final String WEB_ROOT = System.getProperty( "user.dir") + File.separator + "webroot" ; ClassLoader classLoader = null ; Container container = null ; public SimpleLoader() { try { URL[] urls = new URL[1 ]; URLStreamHandler streamHandler = null ; File classPath = new File(WEB_ROOT); String repository = ( new URL("file", null , classPath.getCanonicalPath() + File.separator)).toString() ; urls[ 0] = new URL( null , repository, streamHandler); classLoader = new URLClassLoader(urls); } catch (IOException e) { System.out.println(e.toString() ); } } public ClassLoader getClassLoader() { return classLoader; } public Container getContainer() { return container; } // 這里省略其余代碼 }
基礎閥是干嘛的呢,具體來說是調用具體servlet的service()方法(管道持有對基礎閥的引用)
public class SimpleWrapperValve implements Valve, Contained { protected Container container; public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { SimpleWrapper wrapper = (SimpleWrapper) 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; // Allocate a servlet instance to process this request try { servlet = wrapper.allocate(); if (hres!= null && hreq!= null ) { servlet.service(hreq, hres); } else { servlet.service(sreq, sres); } } catch (ServletException e) { } } public String getInfo() { return null ; } public Container getContainer() { return container; } public void setContainer(Container container) { this .container = container; } }
其他額外添加的閥本人就不在具體描述了,至此SimpleWrapper容器分析完畢!
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創?
轉載請注明出處 博客園 刺猬的溫馴?
本人郵箱: chenying998179 # 163.com ( #改為@ )
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
