望城区网站建设,wordpress导入媒体失败,家具网站建设策划,wordpress加广告【0】README0.0#xff09;本文部分文字描述转自#xff1a;“深入剖析tomcat”#xff0c;旨在学习 tomcat(5)servlet容器 的基础知识#xff1b;0.1#xff09;intro to servlet容器#xff1a;servlet容器是用来处理请求servlet资源#xff0c;并为web客户端填充resp…【0】README0.0本文部分文字描述转自“深入剖析tomcat”旨在学习 tomcat(5)servlet容器 的基础知识 0.1intro to servlet容器servlet容器是用来处理请求servlet资源并为web客户端填充response 对象的模块 0.2补充吃个饭过来真心没想到这么多人围观再次对文章重新排版并不是博主我不给力是CSDN编辑器时不时吊链子啊oh并添加了测试用例的程序流程图共3张[1604111947] 0.3for complete source code, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter5 0.4温馨建议建议阅读本文之前已阅读过 tomcat(1~4)的系列文章因为它们是环环相扣的 1intro to servlet容器servlet容器是用来处理请求servlet资源并为web客户端填充response 对象的模块1.1servlet容器是 org.apache.catalina.Container接口的实例1.2在Tomcat中共有四种容器typest1Enginet2Hostt3Contextt4Wrapper【1】Container接口1在Tomcat中共有四种容器types干货——Tomcat中共有4种容器 t1Engine表示整个Catalina servlet 引擎 t2Host表示包含有一个或多个 Context容器的虚拟主机 t3Context表示一个web 应用程序一个Context 可以有多个 Wrapper t4Wrapper表示一个独立的servlet 2以上4种容器都是 org.apache.catalina包下的接口分别为EngineHost Context Wrapper他们都继承自Container接口。这4个接口的标准实现是 StandardEngine类StandardHost类StandardContext类StandardWrapper类他们都在 org.apache.catalina.core 包内 Attention A1所有的实现类都继承自抽象类 ContainerBase A2Container接口的设计满足以下条件在部署应用时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方法后容器会将处理工作交由管道完成而管道会调用其中的第一个阀开始处理。当第一个阀处理完后它会调用后续的阀继续执行任务直到管道中所有的阀都处理完成。 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 removeValve(Valve valve) {}// this class is copied from org.apache.catalina.core.StandardPipeline classs// 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; // init stage 0.stage stage 1;// Invoke the requested Valve for the current request threadif (subscript valves.length) {valves[subscript].invoke(request, response, this); // 若非基础阀是 ClientIPLoggerValve}else if ((subscript valves.length) (basic ! null)) {basic.invoke(request, response, this); // this line, 直到基础阀才停止遍历非基础阀}else {throw new ServletException(No valve);}}} // end of inner classpublic 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); // 继续回调 SimplePipeline.invokeNext()直到基础阀System.out.println(Client IP Logger Valve);ServletRequest sreq request.getRequest();System.out.println(sreq.getRemoteAddr()); // this line outputs ip addr to the console.System.out.println(------------------------------------);} 4实现阀的遍历Tomcat引入接口 org.apache.catalina.ValveContext 来实现阀的遍历执行 4.1管道必须保证添加到其中的所有阀和基础阀都被调用一次这是通过调用一个 ValveContext接口实例来实现的。 4.2ValveContext接口中最重要的方法是 invokeNext方法在创建了ValveContext实例后管道会调用ValveContext实例的 invokeNext方法。ValveContext实例会首先调用管道中的 第一个阀第一个阀执行完后会调用后面的阀继续执行。ValveContext实例会将自身传给每个阀因此每个阀都可以调用 ValveContext实例的 invokeNext方法 5org.apache.catalina.core.StandardPipeline类 是所有servlet容器中的Pipeline接口的实现Tomcat4中有一个实现了ValveContext接口的内部类名为StandardPipelineValveContext 6Tomcat5 从 StandardPipeline类中移除了 StandardPipelineValveContext类却使用 org.apache.catalina.core.StandardValveContext类来调用阀 【2.1】Pipeline接口 1对于Pipeline接口首先要提到的一个方法是 invoke方法servlet容器调用invoke方法来开始调用管道中的阀和基础阀 2getBasic和setBasicsetBasic方法将基础阀设置到管道中getBasic获取基础阀干货——管道中可以指定基础阀 3addValve和removeValve新增阀和删除阀干货——在管道中可以新增和删除非基础阀请注意SimplePipe.addValve()下面的 System.arraycopy 方法 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); // 新增阀 key line((Pipeline) wrapper).addValve(valve2); // 新增阀, key lineconnector.setContainer(wrapper);try {connector.initialize(); // 创建服务器套接字connector.start(); // // make the application wait until we press a key.System.in.read();}catch (Exception e) {e.printStackTrace();}}
} public synchronized void addValve(Valve valve) { // SimpleWrapper.addValve()pipeline.addValve(valve);}public void addValve(Valve valve) { // SimplePipeline.addValve()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;}} 【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方法 public interface ValveContext {
public String getInfo();
public void invokeNext(Request request, Response response)throws IOException, ServletException;
} 【2.4】Contained接口public interface Contained {
public Container getContainer();
public void setContainer(Container container);
} 【3】Wrapper接口 1intro to WrapperWrapper级的servlet容器是一个 org.apache.catalina.Wrapper接口的实例表示一个独立的servlet定义。Wrapper接口继承自 Container接口又添加了一些额外的方法。 2Wrapper接口的实现类要负责管理继承servlet类的servlet生命周期即调用 servlet的 init(), service(), destroy()方法 3由于Wrapper已经是最低级的容器了不能再向其中添加子容器干货——Wrapper已经是最低级的servlet容器 4Wrapper接口有两个方法load方法 和 allocate方法 4.1load方法载入并初始化servlet类 4.2allocate方法会分配一个已经初始化的servlet实例 public Servlet allocate() throws ServletException { // SimpleWrapper.allocate() method.// Load and initialize our instance if necessaryif (instancenull) {try {instance loadServlet(); // this line}catch (ServletException e) {throw e;}catch (Throwable e) {throw new ServletException(Cannot allocate a servlet instance, e);}}return instance;}private Servlet loadServlet() throws ServletException { // this line 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(); // this line// Acquire an instance of the class loader to be usedif (loadernull) {throw new ServletException(No loader.);}ClassLoader classLoader loader.getClassLoader(); // this line// 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 Loader getLoader() { // this lineif (loader ! null)return (loader);if (parent ! null)return (parent.getLoader());return (null);} 【4】Context接口 1intro to ContextContext接口是一个web 应用程序一个Context实例可以有一个或多个Wrapper实例作为其子容器 2比较重要的方法 addWrapper() and createWrapper() 【5】Wrapper 应用程序demonstrate how to build a smallest servlet container 1SimpleWrapper类该类实现了Wrapper接口包含一个Pipeline实例并使用Loader实例载入servlet类。Pipeline实例包含一个基础阀和两个额外的阀。 【5.1】SimpleLoader类 1SimpleLoader负责完成类的载入工作它知道servlet类的位置通过调用其getClassLoader可以返回一个 java.lang.ClassLoader实例可以用来搜索servlet类的位置 2SimpleLoader的构造函数会初始化类加载器供 SimpleWrapper实例使用 public class SimpleLoader implements Loader { // return class loaderpublic 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;} 【5.2】SimplePipeline类 1该类最重要的方法是 invoke方法 public class SimplePipeline implements Pipeline {public SimplePipeline(Container container) {setContainer(container);}protected Valve basic null;protected Container container null;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 void invoke(Request request, Response response)throws IOException, ServletException {// Invoke the first Valve in this pipeline for this request(new SimplePipelineValveContext()).invokeNext(request, response);} 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; // init stage 0.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 方法的实现 2getLoader()方法该方法返回一个用于载入servlet 类的载入器。若Wrapper实例已经关联了一个载入器则直接将其返回否则它将返回父容器的载入器。若没有父容器getLoader方法会返回null public Servlet allocate() throws ServletException {// Load and initialize our instance if necessaryif (instancenull) {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 (loadernull) {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;} 3SimpleWrapper类有一个Pipeline实例并该为Pipeline实例设置基础阀 【5.4】SimpleWrapperValve类 1SimpleWrapperValve是一个基础阀用于处理对SimpleWrapper类的请求其最主要的方法是 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) {} } 【5.5】ClientIPLoggerValve类 1ClientIPLoggerValve类所表示的阀用来将client的IP 地址输出到控制台上 2注意其invoke方法它先调用方法参数 valveContext的 invokeNext方法来调用管道中的下一个阀。然后它会把几行字符串output到 console 【5.6】HeaderLoggerValve类 1HeaderLoggerValve类作用会把请求头信息output到 console 2注意其invoke方法它先调用方法参数 valveContext的 invokeNext方法来调用管道中的下一个阀。 【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我这里总结了该测试用例的调用流程图共3张 【5.8】运行应用程序 1运行参数 E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\srcjava -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter5.startup/B ootstrap1
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/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.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;q0.8,en;q0.6
------------------------------------
Client IP Logger Valve
127.0.0.1
------------------------------------
Header Logger Valve
host:localhost:8080
connection:keep-alive
accept:text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.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;q0.8,en;q0.6
------------------------------------ 2运行结果 【6】Context应用程序0intro to Context app本app 展示了如何使用一个包含了两个Wrapper实例的Context实例来构建web app 这两个Wrapper 实例包装了两个servlet类当应用程序有多个 Wrapper实例时需要使用一个 映射器。映射器是组件帮助servlet容器Context实例选择一个子容器来处理某个指定的请求1虽然有些应用程序只需要一个servlet但大部分web app 是需要多个servlet合作的。这些应用程序中需要的servlet容器是Context不是Wrapper2本应用程序的映射器是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); // 返回要处理某个特定请求的子容器的实例
}3SimpleContext类是Context容器的一个实例它使用了SimpleContextMapper 类的实例作为其映射器将SimpleContextValve 的实例作为基础阀干货——SimpleContextValve 的实例作为基础阀基础阀非常重要4Context容器中额外添加了两个阀ClinetIPLoggerValve 和 HeaderLoggerValve并包含两个 Wrapper 实例作为其子容器二者都是 SimpleWrapper 实例这两个Wrapper实例使用 SimpleWrapperValve 实例作为其基础阀不再添加其他阀干货——非基础阀如何在管道中遍历基础阀和非基础阀遍历我想我已经说的狗明白了不明白的朋友看本文章节【2】的管道任务的文字描述5剩下的内容包括step1容器包含一个管道容器的invoke方法会调用管道的invoke方法step2管道的invoke方法会调用所有添加到其容器中的阀然后再调用其基础阀的invoke方法step3在Wrapper实例中 基础阀负责载入相关联的servlet类并对请求进行响应step4在包含子容器的 Context实例中 基础阀使用映射器来查找一个子容器该子容器负责处理接收到的请求。若找到了相应的子容器则调用其invoke方法转到step1继续执行6下面对上述的steps 做 detailed introstep1SimpleContext类的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方法}step3SimpleContext类中基础阀是 SimpleContextValve类的实例。在SimpleContextValve类的 invoke方法中 SimpleContextValve实例使用了 Context实例的映射器来查找 Wrapper容器 public class SimpleContextValve implements Valve, Contained {protected Container container;public void invoke(Request request, Response response, ValveContext valveContext)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); // the core line span stylefont-family: SimSun; font-size: 18px; line-height: 24px;使用了 Context实例的映射器来查找 Wrapper容器/span}public Container map(Request request, boolean update) { // SimpleContextMapper.map()// 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); // this lineif (name ! null)wrapper (Wrapper) context.findChild(name); // and this linereturn (wrapper);}
}public String findServletMapping(String pattern) { // SimpleContext.findServletMapping()synchronized (servletMappings) {return ((String) servletMappings.get(pattern));// protected HashMap servletMappings new HashMap();}}
public Container findChild(String name) { // SimpleContext.findChild()if (name null)return (null);synchronized (children) { // Required by post-start changesreturn ((Container) children.get(name));// protected HashMap children new HashMap();}}AttentionA1Wrapper实例的管道会调用 SimpleWrapperValve类的 invoke方法它会分配servlet实例并调用其 service方法A2Wrapper实例中并没有与载入器相关联但是Context 实例关联了类载入器因此SimpleWrapper类的 getLoader() 方法会返回父容器的载入器【6.1】SimpleContextValve类1该类是 SimleContext的基础阀最重要的方法是invoke方法【6.2】SimpleContextMapper类1map方法需要两个参数一个request对象和一个布尔变量。2在本app中 忽略了第2个参数。map() 方法会从request对象中解析出请求的上下文路径并调用 Conetext 实例的findServletMapping() 方法 来获取一个与该路径相关联的名称如果找到了这个名称则它调用 Context实例的findChild方法获取一个 Wrapper 实例【6.3】SimpleContext类1intro to SimpleContext:该类是 Context容器 的实例是与连接器相关联的主容器2本应用程序有两种URL模式用来调用两个 Wrapper实例如/Primitive 和 /Modern 模式当然也可以将多个 URL模式映射到一个Wrapper实例上。只需要添加这些模式即可3SimpleContext类必须实现 Container 和 Context接口实现的方法包括以下几个methodsmethod1addServletMapping()添加一个 URL模式 / Wrapper实例的名称对通过给定的名称添加用于调用Wrapper实例的每种模式method2findServletMapping()通过URL模式 查找对应的Wrapper 实例名称该方法用来查找某个特殊URL 模式对应的Wrapper实例method3addMapper()在Context容器中添加一个映射器。SimpleContext类声明有两个变量 mapper and mappers 。mapper表示程序使用的默认映射器mappers包含SimpleContext 实例中所有可用的映射器。第一个被添加到 Context容器中的映射器称为默认映射器method4findMapper()找到正确的映射器在 SimpleContext类中它返回默认映射器method5map()返回负责处理当前请求的 Wrapper实例public class SimpleContext implements Context, Pipeline {public SimpleContext() {pipeline.setBasic(new SimpleContextValve());}protected HashMap children new HashMap();protected Loader loader null;protected SimplePipeline pipeline new SimplePipeline(this);protected HashMap servletMappings new HashMap();protected Mapper mapper null;protected HashMap mappers new HashMap();private Container parent null;public void addServletMapping(String pattern, String name) {synchronized (servletMappings) {servletMappings.put(pattern, name);}}public String findServletMapping(String pattern) {synchronized (servletMappings) {return ((String) servletMappings.get(pattern));}}public Loader getLoader() {if (loader ! null)return (loader);if (parent ! null)return (parent.getLoader());return (null);}public void addChild(Container child) {child.setParent((Container) this);children.put(child.getName(), child);}public void addMapper(Mapper mapper) {// this method is adopted from addMapper in ContainerBase// the first mapper added becomes the default mappermapper.setContainer((Container) this); // May throw IAEthis.mapper mapper;synchronized(mappers) {if (mappers.get(mapper.getProtocol()) ! null)throw new IllegalArgumentException(addMapper: Protocol mapper.getProtocol() is not unique);mapper.setContainer((Container) this); // May throw IAEmappers.put(mapper.getProtocol(), mapper);if (mappers.size() 1)this.mapper mapper;elsethis.mapper null;}}public Container findChild(String name) {if (name null)return (null);synchronized (children) { // Required by post-start changesreturn ((Container) children.get(name));}}public Container[] findChildren() {synchronized (children) {Container results[] new Container[children.size()];return ((Container[]) children.values().toArray(results));}}public ContainerListener[] findContainerListeners() {return null;}public Mapper findMapper(String protocol) {// the default mapper will always be returned, if any,// regardless the value of protocolif (mapper ! null)return (mapper);elsesynchronized (mappers) {return ((Mapper) mappers.get(protocol));}}public void invoke(Request request, Response response)throws IOException, ServletException {pipeline.invoke(request, response);}public Container map(Request request, boolean update) {//this method is taken from the map method in org.apache.cataline.core.ContainerBase//the findMapper method always returns the default mapper, if any, regardless the//requests protocolMapper mapper findMapper(request.getRequest().getProtocol());if (mapper null)return (null);// Use this Mapper to perform this mappingreturn (mapper.map(request, update));}【6.4】BootStrap2step1首先实例化Tomcat的默认连接器创建两个Wrapper实例并指定名称。 step2main() 方法创建一个 SimpleContext实例并将 wrapper1 和 wrapper2 作为子容器添加到 SimpleContext 实例中。此外它还会实例化两个阀ClientIPLoggerValve 和 HeaderLoggerValve并将它们添加到 SimpleContext实例中step3接下来它会从SimpleMapper类创建一个映射器对象将其添加到 SimpleContext 实例中。映射器负责查找Context 实例中的子容器来处理 HTTP请求step4要载入servlet类还需要一个载入器。并将其添加到 Context实例中。Wrapper实例可以通过 其 getLoader方法来获取载入器因为Wrapper实例是 Context实例的子容器step5添加servlet映射。为 两个Wrapper 实例添加两种模式step6将Context容器与 连接器相关联并初始化连接器调用其 start方法public final class Bootstrap2 { public static void main(String[] args) {HttpConnector connector new HttpConnector(); Wrapper wrapper1 new SimpleWrapper(); // 最低级的servlet容器 Wrapper利用加载器动态加载和封装servletwrapper1.setName(Primitive);wrapper1.setServletClass(servlet.PrimitiveServlet);Wrapper wrapper2 new SimpleWrapper();wrapper2.setName(Modern);wrapper2.setServletClass(servlet.ModernServlet);Context context new SimpleContext(); // 比Wrapper高一级的容器 Context,可以context.addChild(wrapper1);context.addChild(wrapper2);Valve valve1 new HeaderLoggerValve(); // 非基础阀Valve valve2 new ClientIPLoggerValve(); // 非基础阀((Pipeline) context).addValve(valve1);((Pipeline) context).addValve(valve2); // 添加基础阀到 ContextMapper mapper new SimpleContextMapper(); // 映射器mapper.setProtocol(http);context.addMapper(mapper); // 将映射器添加到 contextLoader loader new SimpleLoader(); // 类加载器context.setLoader(loader); // 将该类加载器设置到context容器中// context.addServletMapping(pattern, name);context.addServletMapping(/Primitive, Primitive); // 添加servlet访问路径映射(put到HashMap中)context.addServletMapping(/Modern, Modern);connector.setContainer(context); // 设置容器到Tomcat 连接器try {connector.initialize();connector.start();// make the application wait until we press a key.System.in.read();}catch (Exception e) {e.printStackTrace();}}
}Attention我这里还是总结了一张 Context应用程序的流程图本图跨度比较大请结合上述的3张流程图的调用过程来看protected HashMap children new HashMap(); // servlet资源名称 和 具体servlet资源URI类加载器加载路径 的映射集合。
protected HashMap servletMappings new HashMap();// 访问路径和servlet资源名称的映射集合protected HashMap mappers new HashMap(); // 映射器集合一个Context容器可以有多个映射器
以上代码的调用stepsclient发出HTTP请求请求资源的URI如 http://localhost:8080/Modern 后server的处理过程step1HttpConnector类的大致工作 step1.1initialize方法调用该类的open方法创建服务器套接字 step1.2start方法开启一个线程该线程中有一个while循环不断接收client发送的HTTP连接请求接着调用其类的createProcessor方法 step1.3createProcessor方法调用其类的 newProcessor方法 step1.4newProcessor方法创建HttpProcessorHTTP连接器的支持类HTTP请求处理器利用HttpProcessor实例开启一个线程调用 HttpProcessor.run()方法转向HttpProcessor类的run方法干货中的干货——也即当clients 发出http 连接请求后HttpConnector 在while循环中创建HttpConnector的支持类 HttpProcessorHttp处理器类并调用该类的start方法开启线程即while循环为每一个client 请求 开启一个线程进行处理多么巧妙 step2HttpProcessor类的大致工作 step2.1run方法传入套接字参数并调用process方法 step2.2process方法依次调用 parseConnection()方法 parseRequest()方法 parseHeader() 方法调用上述三个方法后会调用连接器HttpConnector实例的关联容器的invoke方法转向container的invoke方法注意本程序的container是SimpleContext类下面转向SimpleContext.invoke() 方法 step3SimpleContext.invoke() 方法注意SimpleContext中的构造器设置了其关联管道的基础阀是SimpleContextValve基础阀非常重要调用 pipeline.invoke方法step3.1pipeline.invoke()方法会遍历非基础阀非基础阀集合就是一个阀数组直到遍历到基础阀SimpleContextValve调用基础阀SimpleContextValve 的 invoke方法step3.2SimpleContextValve.invoke()方法调用map 查询到请求URI对应的Wrapper并返回返回SimpleWrapper并调用SimpleWrapper.invoke方法step4SimpleWrapper.invoke()方法注意SimpleWrapper中的构造器设置了其关联管道的基础阀是SimpleWrapperValve基础阀非常重要step4.1pipeline.invoke()方法会遍历非基础阀SimpleWrapper关联管道中根本就没有非基础阀只有基础阀SimpleWrapperValve直到遍历到基础阀SimpleContextValve调用基础阀SimpleWrapperValve 的 invoke方法step4.2非基础阀SimpleWrapperValve.invoke方法调用其关联wrapperSimpleWrapper的allocate方法该方法就是返回请求的servlet实例返回实例后调用该servlet实例的 service方法SimpleWrapper.allocate方法的补充该方法先创建一个类加载器然后根据HttpProcessor.parseRequest 解析出的 URI 来加载对应的 servlet实例并返回该实例为什么我说Container容器是这个类Wrapper容器是那个类因为启动程序Bootstrap2.main 方法中对变量的定义已经把它们的关系说的很明白了在接下来的【6.5】章节我再次po出 Bootstrap2.java 的源代码。Attention关于基础阀A1基础阀是非常重要的东西是管道遍历阀基础阀和非基础阀的终点A2需要区分的是SimpleContext设置其关联管道的基础阀是 SimpleContextValve而SimpleWrapper设置其关联管道的基础阀是 SimpleWrapperValve不要一根筋的认为所有容器关联的管道的基础阀都是一样的定要万分留意补充调用map 查询到请求URI对应的Wrapper在映射器中查找与URI对应的 Wrapper的steps step1SimpleContextMapper.map方法 先调用context.findServletMapping方法传入parseRequest解析出的请求路径中的URI 到 servletMappings查找该URI对应的servlet名称step2查找到servlet名称后调用context.findChild()方法传入servlet名称 到 children中查找对应的Wrapper容器要知道Wrapper容器封装了具体的servlet容器Wrapper【6.5】运行应用程序0Bootstrap2.java 源代码public final class Bootstrap2 {public static void main(String[] args) {HttpConnector connector new HttpConnector();Wrapper wrapper1 new SimpleWrapper(); // 最低级的servlet容器 Wrapper利用加载器动态加载和封装servletwrapper1.setName(Primitive);wrapper1.setServletClass(servlet.PrimitiveServlet);Wrapper wrapper2 new SimpleWrapper();wrapper2.setName(Modern);wrapper2.setServletClass(servlet.ModernServlet);Context context new SimpleContext(); // 比Wrapper高一级的容器 Context,可以context.addChild(wrapper1);context.addChild(wrapper2);Valve valve1 new HeaderLoggerValve(); // 非基础阀Valve valve2 new ClientIPLoggerValve(); // 非基础阀((Pipeline) context).addValve(valve1);((Pipeline) context).addValve(valve2); // 添加非基础阀到 ContextMapper mapper new SimpleContextMapper(); // 映射器mapper.setProtocol(http);context.addMapper(mapper); // 将映射器添加到 contextLoader loader new SimpleLoader(); // 类加载器context.setLoader(loader); // 将该类加载器设置到context容器中// context.addServletMapping(pattern, name);context.addServletMapping(/Primitive, Primitive); // 添加servlet访问路径映射(put到HashMap中)context.addServletMapping(/Modern, Modern);connector.setContainer(context); // 设置容器到Tomcat 连接器try {connector.initialize();connector.start();// make the application wait until we press a key.System.in.read();}catch (Exception e) {e.printStackTrace();}}
} 1运行参数E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\srcjava -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/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.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;q0.8,en;q0.6
------------------------------------
init
from service
Client IP Logger Valve
127.0.0.1
------------------------------------2运行结果