【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
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)运行结果
