tomcat(5)servlet容器
生活随笔
收集整理的這篇文章主要介紹了
tomcat(5)servlet容器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【0】README
0.0)本文部分文字描述轉自:“深入剖析tomcat”,旨在學習?tomcat(5)servlet容器?的基礎知識;
0.1)intro to servlet容器:servlet容器是用來處理請求servlet資源,并為web客戶端填充response 對象的模塊;
0.2)補充:吃個飯過來,真心沒想到這么多人圍觀,再次對文章重新排版(并不是博主我不給力,是CSDN編輯器時不時吊鏈子啊,oh),并添加了測試用例的程序流程圖(共3張)[1604111947];
0.3)通知:本文由于CSDN編輯器掉鏈子使得博文排版亂八七糟,故晚輩我又重新進行了排版,請大家移步到?tomcat(5)servlet容器(lastest version),本文明天也會刪除[1604112328]。
【1】Container接口 1)在Tomcat中,共有四種容器(types):(干貨——Tomcat中共有4種容器) t1)Engine:表示整個Catalina servlet 引擎; t2)Host:表示包含有一個或多個 Context容器的虛擬主機; t3)Context:表示一個web 應用程序,一個Context 可以有多個 Wrapper; t4)Wrapper:表示一個獨立的servlet; 2)以上4中容器都是 org.apache.catalina包下的接口:分別為Engine,Host, Context, Wrapper,他們都繼承自Container接口。這4個接口的標準實現是 StandardEngine類,StandardHost類,StandardContext類,StandardWrapper類,他們都在 org.apache.catalina.core 包內; Attention) A1)所有的實現類都繼承自抽象類 ContainerBase ; A2)Container接口的設計滿足以下條件:在部署應用時,Tomcat管理員可以通過編輯配置文件(server.xml)來決定使用哪種容器。這是通過引入容器中的管道(pipeline)和閥(valve)的集合實現的;(干貨——引入了容器中的管道和閥) 【2】管道任務 1)本節旨在說明:當連接器調用了servlet容器的invoke方法后會發生什么事情,并討論org.apache.catalina 包中的4個相關接口,Pipeline, Valve, ValveContext 和 Contained; 2)管道和閥: 2.1)管道:包含該servlet容器將要調用的任務; 2.2)一個閥:表示一個具體的任務。 2.3)在servlet容器的管道中,有一個基礎閥,但是,可以添加任意數量的閥。閥的數量指的是額外添加的閥數量,即,不包括基礎閥。有意思的是, 可以通過編輯tomcat 的 配置文件(server.xml)來動態地添加閥; 2.4)一條管道和閥的示意圖如下:
Attention) A1)管道就想過濾器鏈條一樣,而閥則好似過濾器; A2)當一個閥執行完成后,會調用下一個閥繼續執行。基礎閥總是最后一個執行;(干貨——當一個閥執行完成后,會調用下一個閥繼續執行。基礎閥總是最后一個執行) 3)管道的invoke方法:一個servlet容器可以有一條管道,當調用了容器的invoke方法后,容器會將處理工作交由管道完成,而管道會調用其中的第一個閥開始處理。當第一個閥處理完后,它會調用后續的閥繼續執行任務,直到管道中所有的閥都處理完成。下面是invoke方法的偽代碼: import java.io.IOException; import java.io.PrintWriter; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Locale; import java.util.ResourceBundle;
import java.io.IOException; import java.io.PrintWriter; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Locale; import java.util.ResourceBundle; // invoke each valve added to the pipeline,先是非基礎閥調用 invoke方法 for(;;){valve[i].invoke(); } // then, invoke the basic valve, 后是基礎閥調用 invoke方法(基礎閥最后一個調用invoke方法) basicValve.invoke(...);public void invoke(Request request, Response response) // SimplePipeline.invoke()throws IOException, ServletException {// Invoke the first Valve in this pipeline for this request(new SimplePipelineValveContext()).invokeNext(request, response);} public void invokeNext(Request request, Response response) // SimplePipeline.invokeNext()throws IOException, ServletException {int subscript = stage;stage = stage + 1;// Invoke the requested Valve for the current request threadif (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
Attention)我這里總結了該測試用例的調用流程圖 【5.8】運行應用程序 1)運行參數 E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter5.startup/Bootstrap1 HttpConnector Opening server socket on all host IP addresses HttpConnector[8080] Starting background thread ModernServlet -- init Client IP Logger Valve 127.0.0.1 ------------------------------------ Header Logger Valve host:localhost:8080 connection:keep-alive accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8,en;q=0.6 ------------------------------------ Client IP Logger Valve 127.0.0.1 ------------------------------------ Header Logger Valve host:localhost:8080 connection:keep-alive accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8,en;q=0.6 ------------------------------------
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
【1】Container接口 1)在Tomcat中,共有四種容器(types):(干貨——Tomcat中共有4種容器) t1)Engine:表示整個Catalina servlet 引擎; t2)Host:表示包含有一個或多個 Context容器的虛擬主機; t3)Context:表示一個web 應用程序,一個Context 可以有多個 Wrapper; t4)Wrapper:表示一個獨立的servlet; 2)以上4中容器都是 org.apache.catalina包下的接口:分別為Engine,Host, Context, Wrapper,他們都繼承自Container接口。這4個接口的標準實現是 StandardEngine類,StandardHost類,StandardContext類,StandardWrapper類,他們都在 org.apache.catalina.core 包內; Attention) A1)所有的實現類都繼承自抽象類 ContainerBase ; A2)Container接口的設計滿足以下條件:在部署應用時,Tomcat管理員可以通過編輯配置文件(server.xml)來決定使用哪種容器。這是通過引入容器中的管道(pipeline)和閥(valve)的集合實現的;(干貨——引入了容器中的管道和閥) 【2】管道任務 1)本節旨在說明:當連接器調用了servlet容器的invoke方法后會發生什么事情,并討論org.apache.catalina 包中的4個相關接口,Pipeline, Valve, ValveContext 和 Contained; 2)管道和閥: 2.1)管道:包含該servlet容器將要調用的任務; 2.2)一個閥:表示一個具體的任務。 2.3)在servlet容器的管道中,有一個基礎閥,但是,可以添加任意數量的閥。閥的數量指的是額外添加的閥數量,即,不包括基礎閥。有意思的是, 可以通過編輯tomcat 的 配置文件(server.xml)來動態地添加閥; 2.4)一條管道和閥的示意圖如下:
Attention) A1)管道就想過濾器鏈條一樣,而閥則好似過濾器; A2)當一個閥執行完成后,會調用下一個閥繼續執行。基礎閥總是最后一個執行;(干貨——當一個閥執行完成后,會調用下一個閥繼續執行。基礎閥總是最后一個執行) 3)管道的invoke方法:一個servlet容器可以有一條管道,當調用了容器的invoke方法后,容器會將處理工作交由管道完成,而管道會調用其中的第一個閥開始處理。當第一個閥處理完后,它會調用后續的閥繼續執行任務,直到管道中所有的閥都處理完成。下面是invoke方法的偽代碼: import java.io.IOException; import java.io.PrintWriter; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Locale; import java.util.ResourceBundle;
import java.io.IOException; import java.io.PrintWriter; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Locale; import java.util.ResourceBundle; // invoke each valve added to the pipeline,先是非基礎閥調用 invoke方法 for(;;){valve[i].invoke(); } // then, invoke the basic valve, 后是基礎閥調用 invoke方法(基礎閥最后一個調用invoke方法) basicValve.invoke(...);public void invoke(Request request, Response response) // SimplePipeline.invoke()throws IOException, ServletException {// Invoke the first Valve in this pipeline for this request(new SimplePipelineValveContext()).invokeNext(request, response);} public void invokeNext(Request request, Response response) // SimplePipeline.invokeNext()throws IOException, ServletException {int subscript = stage;stage = stage + 1;// Invoke the requested Valve for the current request threadif (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
?
4)實現閥的遍歷:Tomcat引入接口 org.apache.catalina.ValveContext 來實現閥的遍歷執行; 4.1)管道必須保證添加到其中的所有閥和基礎閥都被調用一次:這是通過調用一個 ValveContext接口實例來實現的。 4.2)ValveContext接口中最重要的方法是 invokeNext方法:在創建了ValveContext實例后,管道會調用ValveContext實例的 invokeNext方法。ValveContext實例會首先調用管道中的 第一個閥,第一個閥執行完后,會調用后面的閥繼續執行。ValveContext實例會將自身傳給每個閥,因此,每個閥都可以調用 ValveContext實例的 invokeNext方法; 5)org.apache.catalina.core.StandardPipeline類:是所有servlet容器中的Pipeline接口的實現,Tomcat4中有一個實現了ValveContext接口的內部類,名為StandardPipelineValveContext; 6)Tomcat5 從 StandardPipeline類中移除了 StandardPipelineValveContext類:卻使用 org.apache.catalina.core.StandardValveContext類來調用閥; 【2.1】Pipeline接口 1)對于Pipeline接口:首先要提到的一個方法是 invoke方法,servlet容器調用invoke方法來開始調用管道中的閥和基礎閥; 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); }? 2)getBasic和setBasic:setBasic方法將基礎閥設置到管道中,getBasic獲取基礎閥;(干貨——管道中可以指定基礎閥) 3)addValve和removeValve:新增閥和刪除閥;(干貨——在管道中可以新增和刪除非基礎閥) 【2.2】Valve接口 1)閥是Valve接口的實例,用來處理接收到的請求,有兩個方法:invoke方法和getinfo方法; public interface Valve {public String getInfo(); public void invoke(Request request, Response response,ValveContext context) throws IOException, ServletException;?
【2.3.】ValveContext接口 1)有兩個方法:invokeNext方法和 getInfo方法; 【2.4】Contained接口 public interface Contained { public Container getContainer();public void setContainer(Container container); }? 【3】Wrapper接口 1)intro to Wrapper:?Wrapper級的servlet容器是一個 org.apache.catalina.Wrapper接口的實例,表示一個獨立的servlet定義。Wrapper接口繼承自 Container接口,又添加了一些額外的方法。 public interface Wrapper extends Container { public long getAvailable();public void setAvailable(long available);public String getJspFile(); public void setJspFile(String jspFile); public int getLoadOnStartup(); public void setLoadOnStartup(int value); public String getRunAs(); public void setRunAs(String runAs); public String getServletClass(); public void setServletClass(String servletClass); public boolean isUnavailable(); public void addInitParameter(String name, String value); public void addInstanceListener(InstanceListener listener); public void addSecurityReference(String name, String link); public Servlet allocate() throws ServletException; public void deallocate(Servlet servlet) throws ServletException; public String findInitParameter(String name); public String[] findInitParameters(); public String findSecurityReference(String name); public String[] findSecurityReferences(); public void load() throws ServletException; public void removeInitParameter(String name); public void removeInstanceListener(InstanceListener listener); public void removeSecurityReference(String name); public void unavailable(UnavailableException unavailable); public void unload() throws ServletException; }?
2)Wrapper接口的實現類:要負責管理繼承servlet類的servlet生命周期,即,調用 servlet的 init(), service(), destroy()方法; 3)由于Wrapper已經是最低級的容器了,不能再向其中添加子容器;(干貨——Wrapper已經是最低級的servlet容器) 4)Wrapper接口有兩個方法:load方法 和 allocate方法; 4.1)load方法:載入并初始化servlet類; 4.2)allocate方法:會分配一個已經初始化的servlet實例; 【4】Context接口 1)intro to Context:Context接口是一個web 應用程序,一個Context實例可以有一個或多個Wrapper實例作為其子容器; 2)比較重要的方法:?addWrapper() and createWrapper(); 【5】Wrapper 應用程序(demonstrate how to build a smallest servlet container) 1)SimpleWrapper類:該類實現了Wrapper接口,包含一個Pipeline實例,并使用Loader實例載入servlet類。Pipeline實例包含一個基礎閥和兩個額外的閥。 【5.1】SimpleLoader類 1)SimpleLoader:負責完成類的載入工作,它知道servlet類的位置,通過調用其getClassLoader可以返回一個 java.lang.ClassLoader實例,可以用來搜索servlet類的位置; 2)SimpleLoader的構造函數:會初始化類加載器,供 SimpleWrapper實例使用; 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() );}}?
【5.2】SimplePipeline類 1)該類最重要的方法是 invoke方法; 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 Valvesprotected 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 threadif (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 }?
【5.3】SimpleWrapper類 1)該類實現了Wrapper接口:并提供了 allocate 和 load 方法的實現; 2)getLoader()方法:該方法返回一個用于載入servlet 類的載入器。若Wrapper實例已經關聯了一個載入器,則直接將其返回;否則,它將返回父容器的載入器。若沒有父容器,getLoader方法會返回null; 3)SimpleWrapper類:有一個Pipeline實例,并該為Pipeline實例設置基礎閥; public class SimpleWrapper implements Wrapper, Pipeline {// the servlet instanceprivate Servlet instance = null;private String servletClass;private Loader loader;private String name;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 necessaryif (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 usedif (loader==null) {throw new ServletException("No loader.");}ClassLoader classLoader = loader.getClassLoader();// Load the specified servlet class from the appropriate class loaderClass 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 itselftry {servlet = (Servlet) classClass.newInstance();}catch (Throwable e) {throw new ServletException("Failed to instantiate servlet");}// Call the initialization method of this servlettry {servlet.init(null);}catch (Throwable f) {throw new ServletException("Failed initialize servlet.");}return servlet;}public String getInfo() {return null;}public Loader getLoader() {if (loader != null)return (loader);if (parent != null)return (parent.getLoader());return (null);}?
【5.4】SimpleWrapperValve類 1)SimpleWrapperValve是一個基礎閥:用于處理讀iSimpleWrapper類的請求,其最主要的方法是 invoke方法; 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 requesttry {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;} }?
【5.5】ClientIPLoggerValve類 1)ClientIPLoggerValve類所表示的閥:用來將client的IP 地址輸出到控制臺上; 2)注意其invoke方法:它先調用方法參數 valveContext的 invokeNext方法來調用管道中的下一個閥。然后,它會把幾行字符串output到 console; public class ClientIPLoggerValve implements Valve, Contained {protected Container container;public void invoke(Request request, Response response, ValveContext valveContext)throws IOException, ServletException {// Pass this request on to the next valve in our pipelinevalveContext.invokeNext(request, response);System.out.println("Client IP Logger Valve");ServletRequest sreq = request.getRequest();System.out.println(sreq.getRemoteAddr());System.out.println("------------------------------------");}public String getInfo() {return null;}public Container getContainer() {return container;}public void setContainer(Container container) {this.container = container;} } 【5.6】HeaderLoggerValve類 1)HeaderLoggerValve類作用:會把請求頭信息output到 console; 2)注意其invoke方法:它先調用方法參數 valveContext的 invokeNext方法來調用管道中的下一個閥。 public class HeaderLoggerValve implements Valve, Contained {protected Container container;public void invoke(Request request, Response response, ValveContext valveContext)throws IOException, ServletException {// Pass this request on to the next valve in our pipelinevalveContext.invokeNext(request, response);System.out.println("Header Logger Valve");ServletRequest sreq = request.getRequest();if (sreq instanceof HttpServletRequest) {HttpServletRequest hreq = (HttpServletRequest) sreq;Enumeration headerNames = hreq.getHeaderNames();while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement().toString();String headerValue = hreq.getHeader(headerName);System.out.println(headerName + ":" + headerValue);}}elseSystem.out.println("Not an HTTP Request");System.out.println("------------------------------------");}public String getInfo() {return null;}public Container getContainer() {return container;}public void setContainer(Container container) {this.container = container;} }?
【5.7】Bootstrap1 step1)創建 HttpConnector 和 SimpleWrapper實例,并將需要加載的 servlet name 賦值給 Wrapper實例; step2)創建一個載入器和兩個閥,將載入器設置到Wrapper實例中 ; step3)將上述創建的兩個閥添加到 Wrapper的管道中; step4)將Wrapper 實例設置為 連接器的servlet容器,并初始化并啟動連接器;? public final class Bootstrap1 {public static void main(String[] args) {/* call by using http://localhost:8080/ModernServlet,but could be invoked by any name */HttpConnector connector = new HttpConnector();Wrapper wrapper = new SimpleWrapper();wrapper.setServletClass("servlet.ModernServlet"); // 設置servlet的相對路徑 Loader loader = new SimpleLoader(); // 類加載器Valve valve1 = new HeaderLoggerValve(); // 把請求頭信息output到 consoleValve valve2 = new ClientIPLoggerValve();// 用來將client的IP 地址輸出到控制臺上 wrapper.setLoader(loader);((Pipeline) wrapper).addValve(valve1);((Pipeline) wrapper).addValve(valve2);connector.setContainer(wrapper);try {connector.initialize(); // 創建服務器套接字connector.start(); // // make the application wait until we press a key. System.in.read();}catch (Exception e) {e.printStackTrace();}} }Attention)我這里總結了該測試用例的調用流程圖 【5.8】運行應用程序 1)運行參數 E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter5.startup/Bootstrap1 HttpConnector Opening server socket on all host IP addresses HttpConnector[8080] Starting background thread ModernServlet -- init Client IP Logger Valve 127.0.0.1 ------------------------------------ Header Logger Valve host:localhost:8080 connection:keep-alive accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8,en;q=0.6 ------------------------------------ Client IP Logger Valve 127.0.0.1 ------------------------------------ Header Logger Valve host:localhost:8080 connection:keep-alive accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8,en;q=0.6 ------------------------------------
?
2)運行結果 【6】Context應用程序 0)intro to Context app:本app 展示了如何使用一個包含了兩個Wrapper實例的Context實例來構建web app, 這兩個Wrapper 實例包裝了兩個servlet類,當應用程序有多個 Wrapper實例時,需要使用一個 映射器。映射器是組件,幫助servlet容器(Context實例)選擇一個子容器來處理某個指定的請求; 1)雖然有些應用程序只需要一個servlet,但大部分web app 是需要多個servlet合作的。這些應用程序中,需要的servlet容器是Context,不是Wrapper; 2)本應用程序的映射器:是SimpleContextMapper類的實例,該類實現類Mapper接口,servlet容器可以使用多個 映射器來支持不同的協議。 public interface Mapper {public Container getContainer(); // 返回與該映射器相關聯的servlet容器的實例;public void setContainer(Container container); // 設置與該映射器相關聯的servlet容器;public String getProtocol(); // 返回該映射器負責處理的協議public void setProtocol(String protocol); //指定該映射器負責處理哪種協議public Container map(Request request, boolean update); // 返回要處理某個特定請求的子容器的實例; }?
3)SimpleContext類:是Context容器的一個實例,它使用了SimpleContextMapper 類的實例作為其映射器,將SimpleContextValve 的實例作為基礎閥; 4)Context容器中額外添加了兩個閥:?ClinetIPLoggerValve 和 HeaderLoggerValve,并包含兩個 Wrapper 實例作為其子容器,二者都是 SimpleWrapper 實例;這兩個Wrapper實例使用 SimpleWrapperValve 實例作為其基礎閥,不再添加其他閥; 5)剩下的內容包括: step1)容器包含一個管道,容器的invoke方法會調用管道的invoke方法; step2)管道的invoke方法會調用所有添加到其容器中的閥,然后再調用其基礎閥的invoke方法; step3)在Wrapper實例中, 基礎閥負責載入相關聯的servlet類,并對請求進行響應; step4)在包含子容器的 Context實例中, 基礎閥使用映射器來查找一個子容器,該子容器負責處理接收到的請求。若找到了相應的子容器,則調用其invoke方法,轉到step1繼續執行; 6)下面對上述的steps 做 detailed intro step1)SimpleContext類的invoke方法調用管道的invoke方法: step2)管道SimplePipeline的invoke如下: 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); // 會調用所有添加到Context 實例中的閥,然后再調用基礎閥的invoke方法;}?
step3)SimpleContext類中,基礎閥是 SimpleContextValve類的實例。在SimpleContextValve類的 invoke方法中, SimpleContextValve實例使用了 Context實例的映射器來查找 Wrapper容器; public class SimpleContext implements Context, Pipeline {public SimpleContext() {pipeline.setBasic(new SimpleContextValve());} public void invoke(Request request, Response response, ValveContext valveContext) // SimpleContextValve.invoke()throws IOException, ServletException {// Validate the request and response object typesif (!(request.getRequest() instanceof HttpServletRequest) ||!(response.getResponse() instanceof HttpServletResponse)) {return; // NOTE - Not much else we can do generically}// Disallow any direct access to resources under WEB-INF or META-INFHttpServletRequest hreq = (HttpServletRequest) request.getRequest();String contextPath = hreq.getContextPath();String requestURI = ((HttpRequest) request).getDecodedRequestURI();String relativeURI =requestURI.substring(contextPath.length()).toUpperCase();Context context = (Context) getContainer();// Select the Wrapper to be used for this RequestWrapper wrapper = null;try {wrapper = (Wrapper) context.map(request, true); // attention for this line.}catch (IllegalArgumentException e) {badRequest(requestURI, (HttpServletResponse) response.getResponse());return;}if (wrapper == null) {notFound(requestURI, (HttpServletResponse) response.getResponse());return;}// Ask this Wrapper to process this Requestresponse.setContext(context);wrapper.invoke(request, response);}?
Attention) A1)Wrapper實例的管道會調用 SimpleWrapperValve類的 invoke方法,它會分配servlet實例,并調用其 service方法; A2)Wrapper實例中:并沒有與載入器相關聯,但是Context 實例關聯了類載入器,因此,SimpleWrapper類的 getLoader() 方法會返回父容器的載入器; ? 【6.1】SimpleContextValve類 1)該類是 SimleContext的基礎閥,最重要的方法是invoke方法; 【6.2】SimpleContextMapper類 public class SimpleContextMapper implements Mapper {/*** The Container with which this Mapper is associated.*/private SimpleContext context = null;public Container getContainer() {return (context);}public void setContainer(Container container) {if (!(container instanceof SimpleContext))throw new IllegalArgumentException("Illegal type of container");context = (SimpleContext) container;}public String getProtocol() {return null;}public void setProtocol(String protocol) {}/*** Return the child Container that should be used to process this Request,* based upon its characteristics. If no such child Container can be* identified, return <code>null</code> instead.** @param request Request being processed* @param update Update the Request to reflect the mapping selection?** @exception IllegalArgumentException if the relative portion of the* path cannot be URL decoded*/public Container map(Request request, boolean update) {// Identify the context-relative URI to be mappedString contextPath =((HttpServletRequest) request.getRequest()).getContextPath();String requestURI = ((HttpRequest) request).getDecodedRequestURI();String relativeURI = requestURI.substring(contextPath.length());// Apply the standard request URI mapping rules from the specificationWrapper wrapper = null;String servletPath = relativeURI;String pathInfo = null;String name = context.findServletMapping(relativeURI);if (name != null)wrapper = (Wrapper) context.findChild(name);return (wrapper);} }?
1)map方法需要兩個參數:一個request對象和一個布爾變量。 2)在本app中, 忽略了第2個參數。map() 方法:會從request對象中解析出請求的上下文路徑,并調用 Conetext 實例的findServletMapping() 方法 來獲取一個與該路徑相關聯的名稱,如果找到了這個名稱,則它調用 Context實例的findChild方法獲取一個 Wrapper 實例; 【6.3】SimpleContext類 1)intro to?SimpleContext:?該類是 Context容器 的實例,是與連接器相關聯的主容器; 2)本應用程序有兩種URL模式:用來調用兩個 Wrapper實例,如/Primitive 和 /Modern 模式;當然,也可以將多個 URL模式映射到一個Wrapper實例上。只需要添加這些模式即可; 3)SimpleContext類必須實現 Container 和 Context接口,實現的方法包括以下幾個(methods): method1)addServletMapping():?添加一個 URL模式 / Wrapper實例的名稱對;通過給定的名稱添加用于調用Wrapper實例的每種模式; method2)findServletMapping():通過URL模式 查找對應的Wrapper 實例名稱;該方法用來查找某個特殊URL 模式對應的Wrapper實例; method3)addMapper():在Context容器中添加一個映射器。SimpleContext類聲明有兩個變量: mapper and mappers 。mapper表示程序使用的默認映射器,mappers包含SimpleContext 實例中所有可用的映射器。第一個被添加到 Context容器中的映射器稱為默認映射器; method4)findMapper():找到正確的映射器,在 SimpleContext類中,它返回默認映射器; method5)map():?返回負責處理當前請求的 Wrapper實例; 【6.4】BootStrap2 step1)首先實例化Tomcat的默認連接器,創建兩個Wrapper實例,并指定名稱。 HttpConnector connector = new HttpConnector();Wrapper wrapper1 = new SimpleWrapper();wrapper1.setName("Primitive");wrapper1.setServletClass("servlet.PrimitiveServlet");Wrapper wrapper2 = new SimpleWrapper();wrapper2.setName("Modern");wrapper2.setServletClass("servlet.ModernServlet");?
step2)main() 方法創建一個 SimpleContext實例,并將 wrapper1 和 wrapper2 作為子容器添加到 SimpleContext 實例中。此外,它還會實例化兩個閥:ClientIPLoggerValve 和 HeaderLoggerValve,并將它們添加到 SimpleContext實例中: Context context = new SimpleContext();context.addChild(wrapper1);context.addChild(wrapper2);Valve valve1 = new HeaderLoggerValve();Valve valve2 = new ClientIPLoggerValve();((Pipeline) context).addValve(valve1);((Pipeline) context).addValve(valve2);?
step3)接下來,它會從SimpleMapper類創建一個映射器對象,將其添加到 SimpleContext 實例中。映射器負責查找Context 實例中的子容器來處理 HTTP請求 Mapper mapper = new SimpleContextMapper();mapper.setProtocol("http");context.addMapper(mapper);?
step4)要載入servlet類,還需要一個載入器。并將其添加到 Context實例中。Wrapper實例可以通過 其 getLoader方法來獲取載入器,因為Wrapper實例是 Context實例的子容器: Loader loader = new SimpleLoader();context.setLoader(loader);?
step5)添加servlet映射。為 兩個Wrapper 實例添加兩種模式: // context.addServletMapping(pattern, name);context.addServletMapping("/Primitive", "Primitive");context.addServletMapping("/Modern", "Modern");?
step6)將Context容器與 連接器相關聯,并初始化連接器,調用其 start方法; connector.setContainer(context);try {connector.initialize();connector.start();// make the application wait until we press a key.System.in.read();}?
【6.5】運行應用程序 1)運行參數 E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter5.startup. Bootstrap2 HttpConnector Opening server socket on all host IP addresses HttpConnector[8080] Starting background thread Client IP Logger Valve 127.0.0.1 ------------------------------------ Header Logger Valve host:localhost:8080 connection:keep-alive accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8,en;q=0.6 ------------------------------------ init from service Client IP Logger Valve 127.0.0.1 ------------------------------------ 2)運行結果 ?創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的tomcat(5)servlet容器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 15寸苹果电脑尺寸是多少(苹果电脑15英
- 下一篇: 刑侦破案电视剧推荐(好看的刑侦悬疑电视剧