Tomcat 管道 Pipeline

前言
最近看了《How tomcat works》一书,在Tomcat源码中,管道Pipeline是Tomcat很重要的一个组成部分。在Tomcat各层中都使用到了。要读懂Tomcat源码,理解Pipeline是何如实现是非常重要的。Tomcat总共被分为从上至下总共分为四层,引擎(engine)、主机(Host)、上下文环境(Context)和包装层(Wrapper)。这里主要针对Context和Wrapper来讲解管道Pipeline。

Pipeline
Tomcat 的 Context 层对应着web应用,Wrapper 则对应 Servlet。Context 层可以包括多个 Wrapper,一个 Wrapper 对应着一个 Servlet。Context 层和 Wrapper 层都实现了管道 Pipeline。这个管道Pipeline到底是干什么的呢?其实管道就是对请求做多层处理。一个管道 Pipeline 可以拥有多个子元素 Valve,子元素 Valve 轮流对请求做一番处理,每一个元素对请求做完处理后再把请求传递给下一个子元素 Valve 进行处理。就相当于一个矿泉水流水线有多个工序一样,先放瓶子,然后装水,接着封盖,最后包装,一个工序接着一个工序,直到最后一个工序完成。 
在 Tomcat 中对于管道 Pipe 有几个相关联的类需要介绍。

Valve
管道 Pipeline 中用一个数组或者list保存多个Valve,每一个 valve 都是流水线的一道工序。而每一道工序都是在ValveContext中执行的,ValveContext 是管道 Pipeline 中所有 Valve 的执行环境。下面是 ValveContext 和 Valve 的 UML 图。

Valve 中的 invoke()就是工序的执行,它对 Request 和 Reponse 进行处理。那为什么Valve的 invoke 中要传入V alveContext 对象呢?这是因为 ValveContext 对象的 invokeNext() 是整个管道执行的入口,它会调用第一个 Valve 数组或者 list 中的第一个 Valve 对象执行其 invoke(),当该 Valve 的 invoke() 执行完成后,为了使下一个 Valve 开始执行它的 invoke(), 就必须持有 ValveContext 对象的引用,因为只有 ValveContext 拥有整个管道 Pipeline 的 Valve元素,只有 ValveContext 轮询到第二个 Valve 对象。

Context实现Pipeline
那么 Tomcat 的Context是如何实现Pipeline 的呢?Context 的标准实现类是 StandardContext。StandardContext 继承 ContainerBase 类,而 Container 类实现了管道接口类 Pipeline ,所以也就是 StandardContext 实现了 Pipeline 。 Pipeline 的标准实现类是 StandardPipeline, StandardPipeline 也实现了 Pipeline。 StandardContext 持有 StandardPipeline 对象的引用。为什么 StandardContext 持有 StandardPipeline 对象的引用自己还要实现 Pipeline接口呢?这就是所谓的装饰者设计模式了,其实,StandardContext 对于接口 Pipeline 中的方法实现都是委托给 StandardPipeline 对象实现的。废话这么多,用图展示之间的关系就一目了然了。

当一个请求过来时,它就会调用 StandardContext 的 invoke() 方法,StandardContext 的 invoke() 是委托给 StandardPipeline 实现的,也就是它会调用 StandardPipeline pipeline 的 invole(Request , Reponse) 方法。StandardPipeline 有一个内部类 StandardPipelineValveContext ,它实现了 ValveContext 接口,StandardPipeline 的所有 valve 都是在 StandardPipelineValveContext 中执行的。 所以 StandardPipeline 的invoke(Request, Reponse) 会调用 StandardPipelineValveContext 的 invokeNext(Request, Reponse) 方法。 现在看看 StandardPipelineValveContext 的代码实现。

    protected class StandardPipelineValveContext
        implements ValveContext {


        protected int stage = 0;

        public String getInfo() {
            return info;
        }


        // ----------------------------------------------------- Public Methods

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

        }


    }


public class TestValve implements Valve {
    @Override
    public String getInfo() {
        return null;
    }

    @Override
    public void invoke(Request request, Response response, ValveContext context) throws IOException, ServletException {
            //TODO 对 request 和 Reponse 做相关的操作

            context.invokeNext(request,response);
    }
}
StandardPipelineValveContext 中 subscript 是当前 valve 数组要执行的 valve 下标。 stage 是下一个要执行的 valve 的数组下标。 对于 basic 是什么先不理会。 TestValve 是我实现的一个测试 Valve ,StandardPipelineValveContext 中 valves[subscript].invoke(request, response, this) 会调用 TestValve 的 invoke() , TestValve 的 invoke() 里面 context.invokeNext(request,response) 它会调用 StandardPipelineValveContext 的 involeNext() ,subscript 和 stage 值更新。 这样,valve 数组的 valve 会依顺序执行。于是当请求访问到 Context 这一层时,管道(pipeline) 会对请求做一系列的处理。

为了代码的复用性,StandardContext 对于接口 Pipeline 的方法实现都是放在其父类 ContainerBase 中,然后 StandardContext 继承过来。 
同样,对于包装器 (Wrapper) 也有标准实现,是 StandardWrapper,它的管道(Pipeline) 实现和 StandardContext 基本一致。现在说说 StandardPipelineValveContext 中的 baisc 变量是什么。 它其实也是一个 Valve,它当 valve 数组都执行完后,再执行 basic, basic 的 invoke() 方法中会根据请求 URL 找出对应的 Wrapper 来进行处理。因为一个上下文环境(Context) 拥有对个 Wrapper ,请求最后从 Context 映射到哪个 Wrapper,就是在这个 Basic 的 invoke() 中实现的,当映射到对应的 Wrapper 后,就会执行 Wrapper 的管道(Pipeline) 。于是,请求从Context 的管道执行完成后转移到 Wrapper 的管道中。
--------------------- 
作者:kdk0108 
来源:CSDN 
原文:https://blog.csdn.net/sinat_26158619/article/details/78856215 
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值