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中拦截器的请求流程一(模拟大致流程)>>

http://blog.csdn.net/dlf123321/article/details/40078583

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,用来显示http的请求头,代码从略。

Bootstrap1启动类

public final class Bootstrap1 {
  @SuppressWarnings("deprecation")
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("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容器的使用,我们放在下一节中。

how tomcat works 五 servlet容器 上的更多相关文章

  1. how tomcat works 5 servlet容器 下

    上一节,我们主要说的是Wrapper容器,这一节我们说Context容器. 再重申一遍,一个Context容器可以包含多个Wrapper容器; 一个Wrapper容器就表示一个独立的servlet. ...

  2. Tomcat是一个Servlet容器?

    "Tomcat是一个Servlet容器",这句话对于2019年的程序员应该是耳熟能详的. 单纯的思考一下这句话,我们可以抽象出来这么一段代码: class Tomcat { Lis ...

  3. How Tomcat works — 五、tomcat启动(4)

    前面摆了三节的姿势,现在终于要看到最终tomcat监听端口,接收请求了. 目录 Connector Http11Protocol JIoEndpoint 总结 在前面的初始化都完成之后,进行Conne ...

  4. Web服务器(Apache)与Servlet容器(Tomcat)

    之前一直比较迷惑Apache与Tomcat的关系,通过查询资料,有所了解,现记录于此. Apache与Tomcat 两者定位:Apache是HTTP Web服务器,Tomcat是Web容器. 有一个非 ...

  5. httpServletRequest对象、filter、servlet、servlet容器、catalina、tomcat、以及web容器之间的关系

    学习servlet的时候经常感到疑惑 HttpServletRequest是服务器创建的?还是servlet容器创建的? 过滤器是服务器创建的?还是servlet容器创建的? serlet容器和tom ...

  6. Servlet容器container

    通俗点说,所谓容器,就是放东西的地方.Servlet容器自然就是放Servlet的地方.J2EE开发,是有分工的.一般的程序员,写得都是应用开发,我们会按照一定的规则,开发我们的系统,比如用Servl ...

  7. 为什么要有 Servlet ,什么是 Servlet 容器,什么是 Web 容器?

    本文已收录至 https://github.com/yessimida/yes ,这里有我的所有文章分类汇总,欢迎 star! 以下代码相信大家都很熟悉,大学时学 Java Web 都写过这样的代码. ...

  8. tomcat与jboss等容器的区别

    1.JBoss 是 J2EE 应用服务器,而 Tomcat 只是一个 Servlet 容器,或者说是一个简单的 J2EE 应用服务器. JBoss 中的 Servlet 容器还是 Tomcat. 与  ...

  9. springboot(七) 配置嵌入式Servlet容器

    github代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-service ...

随机推荐

  1. SpringMVC基础配置(通过注解配置,非xml配置)

    SpringMVC是什么,有多火,我这里就不再啰嗦了,SpringMVC比Struts2好用太多,我在学校的时候私下里两种都接触过,对比之后果断选择了SpringMVC,后来在做Android应用开发 ...

  2. Android倒计时器——CountDownTimer

    Android倒计时器--CountDownTimer 说明 第一个参数是倒计时的时间 第二个参数是多长时间执行一次回调 /** * @param millisInFuture The number ...

  3. PGM:贝叶斯网表示之朴素贝叶斯模型naive Bayes

    http://blog.csdn.net/pipisorry/article/details/52469064 独立性质的利用 条件参数化和条件独立性假设被结合在一起,目的是对高维概率分布产生非常紧凑 ...

  4. JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法

    JAVA进阶之旅(一)--增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法 学完我们的java之旅,其实收获还是很多的,但是依然还有很 ...

  5. JAVA面向对象-----main方法详解

    JVM看不懂的可以跳过,这里不做过多解释,(^__^) 嘻嘻-- 主函数是静态的 public static void main(String[] args){ } 主函数是什么:主函数是一个特殊的函 ...

  6. Java基本语法-----java流程控制语句

    1顺序语句 语句:使用分号分隔的代码称作为一个语句. 注意:没有写任何代码只是一个分号的时候,也是一条语句,称作空语句. 顺序语句就是按照从上往下的顺序执行的语句. 2判断(if-else) 在我们找 ...

  7. Java中循环声明变量方法

    Java循环声明变量 之前想这样做,但是网上一直搜索不到,下面是我的方式 项目中 // 得到需要查询外表的数量,然后分别创建缓存,插入数据多的时候如果编码在缓存里面,就不需要再去查询数据库了.key: ...

  8. EBS 客户表结构

     客户表/联系人/PARTY关联 HZ_PARTIES 客户账户表 HZ_CUST_ACCOUNTS SELECT hp.party_number --客户注册标识 , hp.party_name ...

  9. iOS中让Settings Bundle中的变化立即在App中反应出来的两种方法

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 为了能够在Settings Bundle中的变化在进入App后 ...

  10. Android简易实战教程--第五话《开发一键锁屏应用》

    转载请注明出处:http://blog.csdn.net/qq_32059827/article/details/51860900 点击打开链接 Device Administration 对于这个应 ...