《How tomcat works》读书笔记_容器(1)

容器模型主要用于为一个servlet处理request对象,并且为Web客户端返回response对象。容器模型由一个org.apache.catalina.Container接口来表示,一般有四种类型的容器:Engine,Host,Context和Wrapper。这一章节主要讨论Context和Wrapper这两种容器,其它的留在后面的章节讨论。这章首先讨论Container接口,以及在容器中使用管道机制。然后再讨论Context和Wrapper这两个接口。

Container接口

一个容器必须实现org.apache.catalina.Container接口,就如在上一章中,你可以将一个Container通过connector的setContainer方法设置到一个connector中,这样connector就可以调用Container的invoke方法。
在Catalina中,容器被分为四个不同的概念层:

  • Engine,表示整个Catalina的servlet引擎
  • Host,表示拥有数个Context(上下文)的虚拟主机
  • Context,表示一个ServletContext,也就是一个Web应用;一个Context可以包含多个wrapper
  • Wrapper,表示一个单独的Servlet

以上的概念层都会由一个接口来表示。这些接口分别是Engine,Host,Context和Wrapper。所有的这四个接口都继承了Container接口,这四个接口的标准实现为StandardEngine, StandardHost, StandardContext 和StandardWrapper。
UML类图如下:

这里写图片描述

需要注意的是,所以实现类都继承了ContainerBase这个抽象类。

管道任务

这一节主要描述当connector调用了容器container的invoke方法后要做哪些处理。接下来分节讨论四个接口:PipeLine,Value,ValueContext,Contained。

管道(pipeline)包含了容器请求的所有任务,一个Value代表一个指定的任务。在容器的管道中除了你可以任意增加Value以外,还有一个基本的value(Base Value)。管道中Value的数量定义为除了基本value以外的Value的数量。另外,你也可以通过编辑Tomcat的配置文件server.xml来动态增加Value。

管道类似于一个过滤的责任链模式。一个value会处理传递给它的request和response对象。当一个value完成处理时,它会调用管道中的下一个value。基本Value总是在最后被调用。

一个容器拥有一个管道。当一个容器的invoke方法被调用时,容器将处理直接交给管道,管道调用它的第一个Value,然后这个Value调用下一个Value,以此类推,直到管道中再没有任何Value。你可能认为管道中的invoke方法的伪代码是下面这样的:

// invoke each valve added to the pipeline
for (int n=0; n<valves.length; n++) {
    valve[n].invoke( ... );
}
// then, invoke the basic valve
basicValve.invoke( ... );

然而,Tomcat在设计时使用了另外一种方式,它引入了org.apache.catalina.ValveContext接口。

当connector调用了容器的Invoke方法后,容器的invoke方法里并没有做任何处理,直接调用了管道的invoke方法。管道PipeLine接口中的invoke方法同Container接口中的invoke方法是完全相同的。

    public void invoke(Request request, Response response)
        throws IOException, ServletException {
        pipeline.invoke(request, response);
    }

这就是Container接口在其实现类ContainerBase中invoke方法的代码。pipleLine是一个容器内的PipeLine实例。

管道必须确保所有Value,包括基本Value,只被调用一次。管道通过一个ValueContext接口来完成这个任务。ValueContext以管道内部类的形式实现,这样ValueContext就可以使用管道所有的成员变量。ValueContext中比较重要的方法就是invokeNext:

public void invokeNext(Request request, Response response)
    throws IOException, ServletException

容器直接调用了管道的invoke方法,下面是管道的invoke方法:

public void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Invoke the first Valve in this pipeline for this request
        (new StandardPipelineValveContext()).invokeNext(request, response);

    }

在创建了一个ValueContext的实例后,管道直接调用了ValueContext的invokeNext方法。ValueContext会先请求管道中的第一个Value,在第一个Value执行处理之前第一个Value会请求下一个Value。ValueContext会将自己传递给每一个Value,这个每个Value就可以调用它的invokeNext方法。

public void invoke(Request request, Response response,
    ValveContext valveContext) throws IOException,ServletException

这是Value接口中的invoke方法。

public void invoke(Request request, Response response,
    ValveContext valveContext) throws IOException, ServletException {
    // Pass the request and response on to the next valve in our pipeline
    valveContext.invokeNext(request, response);
    // now perform what this valve is supposed to do
...
}

实现方法如上。

        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
                    (sm.getString("standardPipeline.noValve"));
            }

        }

这是StandardPipelineValveContext类中的invokeNext方法的具体实现。invokeNext方法利用变量subscript和stage来记忆哪个Value是已经被请求过的。

上面使用过的三个接口:Pipeline,Value和ValueContext,在接下将详细讨论。

Pipeline接口

在Pipeline接口中我们提到的第一个方法是invoke,容器会调用这个方法来请求管道中的Values以及基本Value。addValue增加一个新的Value,removeValue删除一个Value。你可以通过setBasic方法来设置一个基本Value,getBasic方法可以获取一个基本Value。基本Value是在最后被请求的,它主要处理请求以及相应的response。接口如下:

import java.io.IOException;
import javax.servlet.ServletException;
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);
}

Valve接口

package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;
public interface Valve {
    public String getInfo();
    public void invoke(Request request, Response response,
        ValveContext context) throws IOException, ServletException;
}

Value接口代表一个value,value组件负责处理request请求。这个接口有两个方法:invoke和getInfo。invoke方法上面已经讨论过了。getInfo方法主要是提供该Value实现类的一些信息。

ValveContext接口

package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;
public interface ValveContext {
    public String getInfo();
    public void invokeNext(Request request, Response response)
        throws IOException, ServletException;
}

这个接口也有两个方法:invokeNext和getInfo。

Contained接口

package org.apache.catalina;
public interface Contained {
    public Container getContainer();
    public void setContainer(Container container);
}

Vaule类可以选择性的实现org.apache.catalina.Contained接口,这个接口指定其实现类至多只能与一个容器实例关联。

Wrapper接口

org.apache.catalina.Wrapper接口代表一个wrapper,wrapper是代表一个单独servlet的容器。Wrapper实现了Container接口并添加了一些方法。Wrapper接口主要负责一个servlet生命周期的管理以及底层的servlet类。为一个servlet调用init,service和destroy方法。由于Wrapper是最底层的容器,所以它不可以再增加子容器。如果在Wrapper上调用addChild方法会收到一个IllegalArgumantException异常信息。

Wrapper中有两个重要的方法:allocate和load。allocate方法用于分配一个由wrapper代表的已经初始化完成的servlet。allocate方法同时也要考虑servlet是否实现了javax.servlet.SingleThreadModel接口,这将会在后面的章节讨论。load方法用于加载和初始化一个由wrapper表示的servlet。

public Servlet allocate() throws ServletException {
public void load() throws ServletException;

这是两个方法的签名。
剩下的方法将会在11章讨论。

Context接口

Context也是一个容器,它代表了一个servlet context,也就是代表了一个Web应用或是整个Web工程。一个Context可以有一个或是多个Wrapper作为它的字容器。
比较重要的方法有:addWrapper和createWrapper。在12章我们将详细讨论这个接口。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值