Tomcat4 源代码分析 (5) How Tomcat Works 第五章

 

一个container是一种模块,处理对servlet的请求并且返回response对象给web client端. 一个container由org.apache.catalina.Container接口定义.一共有四种类型的container:engine,host,context和wrapper.本章将会介绍context和wrapper,剩下两种会在13章中介绍.本章开始于对container接口的讨论,接着讲述在container中pipelining的原理.然后具体介绍wrapper和context接口.本章结尾给出了两个应用程序,分别实现了简单的wrapper和context.

 

 1 Container接口

 

Container必须实现org.apache.catalina.Container接口。有四种不同概念层次的container,

Engine:代表整个Catalina servlet引擎。

Host:代表一个虚拟host,其中包含若干context。

Context:代表一个web application,其中可包含若干wrapper。

Wrapper:代表一个独立的servlet。

在org.apache.catalina.core包中,StandardEngine, StandardHost, StandardContext, StandardWrapper是四个标准实现。

 

这四种引擎不都是必须的。比如本章的第一个application只包含一个wrapper。第二个application包含一个context和wrapper。

 

一个Container可以有0个或多个低一层的child container。比如,一个context可以有若干个wrapper,host可以有若干context。因为wrapper在最底层,所以是没有child container的。加减一个孩子的方法在接口中有定义,

public void addChild(Container child)

public void removeChild(Container child)

Container接口还可以查找child container,

public Container findChild(String name)

public Container[ ] findChildren()

 

Container还有一些辅助性的组件,比如Loader, Logger, Manager, Realm, Resources,后面几章会有介绍。Container接口对这些组件有相应的get set方法。

 

需要强调的是,Tomcat管理员可以在configuration文件(server.xml)中设置container的性能。这是通过container中的pipeline和一组valve(阀门,管子)来实现的。下一节“Pipelining Tasks”会有讨论。

 

2、Pipelining Tasks

 

 这部分内容讲解了connector调用的container的invoke方法都具体做了什么。这一节会介绍4个相关的接口:Pipeline, Valve, ValveContext, Contained。他们都在包org.apache.catalina中。

 

Pipeline包含Container调用的所有的任务。一个valve代表一个具体的任务。在container的pipeline中有一个基本的valve,但是你可以加入任意多的valve。valve的个数是指后加入的个数,并不包括那个基本的valve。有趣的是,valve可以动态的加入到Tomcat的configuration文件(server.xml)中。

 

pipeline和valve理解起来很象是servlet filter。pipeline是一个filter chain,每一个valve是一个filter。一个valve的任务完成后,下一个valve的任务会被调用。那个basic valve总是被最后调用。可以想象,这个过程可以用下面的伪代码描述:

for (int n=0;n<valve.length;n++) {

    valve[n].invoke(...);

}

basicValve.invoke(...);

 

但是,Tomcat并没有用上述方式,而是引入了org.apache.catalina.ValveContext接口。Container的invoke()方法被调用时,里面调用了pipeline的invoke()方法。pipeline里的invoke方法有着和Container中一样的定义:

public void invoke(Request request, Response response) throws...

 pipeline作为一个属性关联到一个Container对象。

 

 现在pipeline需要确定每一个关联到他的valve都被执行一次。pipeline通过ValveContext接口的一个实例来完成这个任务。ValveContext作为pipeline的一个内部类实现,所以他可以访问到pipeline中的每一个属性。

 

ValveContext中最重要的方法是:

public void invokeNext(Request request, Response response) throws....

Valve中的invoke()方法为:

public void invoke(Request request, Response response, ValveContext vc)....

 

valveContext的invokeNext先调用第一个valve的invoke方法,并且他自己传给valve的invoke方法,然后再由valve的invoke来调用valveContext的invokeNext()。

 

简言之,valvecontext, pipeline, valve 类之间的关系如下:

class pipeline {

 

    valve[] avalve;

 

    function invoke(request, response) {

        new valvecontext().invokeNext();

    }

 

    inner class valvecontext {

        invokeNext(request, response) {

            avalve[i].invoke(request, response, this);

        }

    }

}

 

class valve {

 

    invoke(request, response, valvecontext) {

        // TO DO ...

        this.invokeNext(request, response);

    }

 

}

pipeline和container的关系是:pipeline是container的一个成员变量

用这种方式,多个valve依次被调用。

 

==========================================

PS: 上述3个类  和  container 又有什么关系?

==========================================

 

3、The Wrapper Application

 

    这里需要提一下将在12章中介绍的Context接口。这个接口代表了一个web application。他通常含有多个wrapper作为他的孩子。重要的方法包括addWrapper, createWrapper...

 

    转入正题。

 

    本章wrapper的一个简单的实现是 ex05.pyrmont.core.SimpleWrapper。包含了一个Pipeline(ex05.pyrmont.core.SimplePipeline), 一个Loader(ex05.pyrmont.core.SimpeLoader)来加载一个servlet。

    这个pipeline中包含了一个基本的valve(SimpleWrapperValve)和两个additional的valve(ClientIPLoggerValve , HeaderLoggerValve)。

    这个loader的工作过程 和 前几章的加载类的过程完全一样。只是功能给抽离出来而已。

 

    SimpeWrapperValve的重要作用。

 

    作为基本的valve。SimpleWrapperValve的做用是 调用 servlet的service方法。方法调用的地方是在valve 的 invoke方法中。

 

    另外两个附加的valve,作用就是在simplewrappervalve调用invoke之前,做点事情。(filter的功能应该就是在这里实现的吧)。

 

      ex05.pyrmont.startup.Bootstrap1 启动类的实现如下:

    HttpConnector connector = new HttpConnector();//新建连结器
    Wrapper wrapper = new SimpleWrapper();//新建容器,初始化方法会设置basicValve
    wrapper.setServletClass("ModernServlet");//设置容器中的servlet
    Loader loader = new SimpleLoader();//新建类装载器
    Valve valve1 = new HeaderLoggerValve();//新建additional valve
    Valve valve2 = new ClientIPLoggerValve();//新建additional valve

    wrapper.setLoader(loader);// 设置容器的类装载器
    ((Pipeline) wrapper).addValve(valve1);//设置容器的additional valve
    ((Pipeline) wrapper).addValve(valve2);//设置容器的additional valve

    connector.setContainer(wrapper);//设置连结器对应的容器

    try {
      connector.initialize();
      connector.start();

    }
    catch (Exception e) {
    }

 

 

4、 The Context Application

 

    这个应用程序展示了包含两个wrapper的容器(也即两个servelt)。一个以上的wrapper就需要引入一种新的组件,叫做mapper。mapper组件的作用是根据特定的request来选择哪个wrapper来处理。

 

    mapper的接口定义如下:

public interface Mapper {

      public Container getContainer();

        public void setContainer(Container container);

      public String getProtocol();

      public void setProtocol(String protocol);

      public Container map(Request request, boolean update);//用来返回一个child container

}

 

    回忆一下,从container层次的角度来说,wrapper在最底层,context在wrapper层之上。

    本章中这个context的例子,用了一个loader和两个valves。这三个组件都和context相关联。这样他们可以被底层的wrapper所共享。这个context被指定为connector的容器。下面列出的是这个容器(即context)的执行顺序:

    a. context有一个pipeline。这个container的invoke方法调用pipeline的invoke方法。

    b. pipeline的invoke方法调用所有context的valve的invoke方法。

    c. 在context层的basic valve中,用mapper找到一个child wrapper来处理特定的请求

    d. 找到了child wrapper后,wrapper的各个valve在被依次调用。

 

    ex05.pyrmont.core.SimpleContextValve

    ex05.pyrmont.core.SimpleContextMapper

    ex05.pyrmont.core.SimpleContext

    这三个类分别就是 context层的basic valve、context的孩子wrapper的影射、context层  的具体的实现类。

 

 

    启动类bootstrap2.java的主要部分如下:

HttpConnector connector = new HttpConnector();//新建连结器
    Wrapper wrapper1 = new SimpleWrapper();//新建一个wrapper
    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
    Wrapper wrapper2 = new SimpleWrapper();//新建另一个wrapper
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");

    Context context = new SimpleContext();//新建一个context
    context.addChild(wrapper1);//为context添加一个wrapper
    context.addChild(wrapper2);//为context再添加一个wrapper

    Valve valve1 = new HeaderLoggerValve();//新建valve
    Valve valve2 = new ClientIPLoggerValve();//新建valve

    ((Pipeline) context).addValve(valve1);// context层添加valve
    ((Pipeline) context).addValve(valve2);// context层添加valve

    Mapper mapper = new SimpleContextMapper();//新建一个mapper
    mapper.setProtocol("http");
    context.addMapper(mapper);
    Loader loader = new SimpleLoader();//新建一个loader
    context.setLoader(loader);
    // context.addServletMapping(pattern, name);
    context.addServletMapping("/Primitive", "Primitive");//建立wrapper的mapping
    context.addServletMapping("/Modern", "Modern");
    connector.setContainer(context);// 为连结器设置容器
    try {
      connector.initialize();
      connector.start();

      // make the application wait until we press a key.
      System.in.read();
    }
    catch (Exception e) {
      e.printStackTrace();
    }

 

 

 运行启动类后,在ie中请求 http://localhost:8080/Modern   http://localhost:8080/Primitive 。容器context接受请求。在执行完context层的valve后,会根据 /Modern /Primitive 来找到对应的wrapper,来继续执行,进而调用具体的servlet。

 

 

-- 完毕 欢迎各位同仁共同交流:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值