how tomcat works 五 servlet容器 上

网友投稿 249 2022-10-30


how tomcat works 五 servlet容器 上

servlet容器是用来处理请求servlet资源,并为Web客户端填充response对象的模块。在上一篇文章(也就是书的第四章)我们设计了SimpleContainer类,让他实现Container接口,也基本完成了容器的作用。但是我们得知道在实际的tomcat中有4类容器: Engine: 表示整个Catalina servlet引擎; Host: 包含一个或多个Context容器的虚拟主机; Context:表示一个Web应用程序,一个Context可以包含多个Wrapper Wrapper: 表示一个独立的Servlet; UML图如下:

Container容器中包含addChild(Container con),removeChild(Container con),findChild(String name),findChildren()四个方法,方法的作用就不解释了,Wrapper就表示一个独立的Servlet所以,它的addChild方法体为空。

在本节,我们主要展示Wrapper的特性,其他的容器以后再说。

首先我们看看时序图(第一次画 可能在时序图规则上有些问题)

管道任务

这节主要说明档连接器调用了servlet容器的invoke方法后发生的事情。

管道包含了一个servlet容器要执行的任务(和本身的servlet无关,是指额外执行的任务)

一个Wrapper会包含一个管道,而一个管道会包含若干个Valve(阀)

public class SimpleWrapper implements Wrapper, Pipeline { // the servlet instance private Servlet instance = null; private String servletClass; private Loader loader; private String name; private SimplePipeline pipeline = new SimplePipeline(this); protected Container parent = null; ...}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]; //所有的阀}

在阀中,请求会像水一样经过管道中的每一个阀,上图中的阀n会调用我们请求的servlet类。

再调用容器的invoke方法后,会到如下的方法

invokeNext是StandardPipelineValveContext类中的方法(StandardPipelineValveContext是管道的一个内部类,因此可以访问管道的所有成员) 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

ok?如水般流过。下面这行代码值得我们仔细看看

valves[subscript].invoke(request, response, this);

也可参阅拙作

<<说说struts2中拦截器的请求流程一(模拟大致流程)>>

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 pipeline valveContext.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); } } else System.out.println("Not an HTTP Request"); System.out.println("------------------------------------"); }}

这行代码 valveContext.invokeNext(request, response); 看懂了吧

另一方面 大家应该能看出来,所有的阀都是逆向执行的,每次执行一个阀的时候,阀都会先传递给后面的阀,等到后面的执行完毕后,才执行自己的业务逻辑。

当通道里的处了最后一个基础阀外,都走了一遍的时候自然就执行如下代码

basic.invoke(request, response, this);

问题是basic是谁?

这个咱们等会再说。

Pipeline接口

我们在这里使用的是SimplePipeline,它实现了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);}

Pipeline里面有个基础阀,是最后调用的,负责处理request对象鱼response对象。因而这里有getset方法

通道里面存放的是阀,所以剩下的方法就不用多说了吧。

另外tomcat4中,还存在一个StandardPipelineValveContext,上面已经说它是SimplePipeline的一个内部类。

Valve接口

public interface Valve {public String getInfo();public void invoke(Request request, Response response,ValveContext context) throws IOException, ServletException;}

很清晰,不是吗?

Contained接口

public interface Contained {public Container getContainer();public void setContainer(Container container);}

实现这个接口的类,至多与一个servlet容器相关联。

Wrapper接口

首先我们在文章开始的时候就说了,Wrapper包装的是一个最基础的servlet

这个接口里面有两个方法比较重要

public javax.servlet.Servlet allocate() throws javax.servlet.ServletException;

public void load() throws javax.servlet.ServletException;

allocate会分配一个已经初始化的servlet实例,

load会载入它。

Wrapper应用程序

类图如下:

SimpleLoader类

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() ); } }}

构造函数会初始会类加载器,供SimpleWrapper使用

SimpleWrapper类

该类实现了 org.apache.catalina.Wrapper 接口并且实现了 allocate 方法和load 方法。

器构造函数如下

public SimpleWrapper() { pipeline.setBasic(new SimpleWrapperValve()); }

上文我们曾问过,basic从哪里来,从这里来!

SimpleWrapperValve类

通过pipeline.setBasic(new SimpleWrapperValve())我们知道SimpleWrapperValve类是一个基础阀,用来处理传递Servlet的参数,如下:

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) { } }

应该没有什么问题。

ClientIPLoggerValve类

是用来显示客户端的ip地址的,代码从略,另外还有HeaderLoggerValve,用来显示final class Bootstrap1 { @SuppressWarnings("deprecation")public static void main(String[] args) {/* call by using but could be invoked by any name */ HttpConnector connector = new HttpConnector(); Wrapper wrapper = new SimpleWrapper(); wrapper.setServletClass("ModernServlet"); Loader loader = new SimpleLoader(); Valve valve1 = new HeaderLoggerValve(); Valve valve2 = new ClientIPLoggerValve(); 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(); } }}

运行结果如下:

看到了,其实在部分里,不管你请求那个服务,返回的结果都一样,为什么?这个还用我来解释吗?

这一章东西比较多,关于context容器的使用,我们放在下一节中。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:测试接口(webservice测试接口)
下一篇:接口测试用例(接口测试用例自动生成工具)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~