Tomcat7.0源码分析——请求原理分析(下)

前言

  本文继续讲解TOMCAT的请求原理分析,建议朋友们阅读本文时首先阅读过《Tomcat7.0源码分析——请求原理分析(上)》《Tomcat7.0源码分析——请求原理分析(中)》。在《Tomcat7.0源码分析——请求原理分析(中)》一文我简单讲到了Pipeline,但并未完全展开,本文将从Pipeline开始讲解请求原理的剩余内容。

管道

  在Tomcat中管道Pipeline是一个接口,定义了使得一组阀门Valve按照顺序执行的规范,Pipeline中定义的接口如下:

  • getBasic:获取管道的基础阀门;
  • setBasic:设置管道的基础阀门;
  • addValve:添加阀门;
  • getValves:获取阀门集合;
  • removeValve:移除阀门;
  • getFirst:获取第一个阀门;
  • isAsyncSupported:当管道中的所有阀门都支持异步时返回ture,否则返回false;
  • getContainer:获取管道相关联的容器,比如StandardEngine;
  • setContainer:设置管道相关联的容器。

  Engine、Host、Context及Wrapper等容器都定义了自身的Pipeline,每个Pipeline都包含一到多个Valve。Valve定义了各个阀门的接口规范,其类继承体系如图1所示。


图1  Valve的类继承体系

这里对图1中的主要部分(LifecycleMBeanBase及Contained接口在《Tomcat7.0源码分析——生命周期管理》一文已详细阐述过)进行介绍:

  • Valve:定义了管道中阀门的接口规范,getNext和setNext分别用于获取或者设置当前阀门的下游阀门,invoke方法用来应用当前阀门的操作。
  • ValveBase:Valve接口的基本实现,ValveBase与Valve的具体实现采用抽象模板模式将管道中的阀门串联起来。
  • StandardEngineValve:StandardEngine中的唯一阀门,主要用于从request中选择其host映射的Host容器StandardHost。
  • AccessLogValve:StandardHost中的第一个阀门,主要用于管道执行结束之后记录日志信息。
  • ErrorReportValve:StandardHost中紧跟AccessLogValve的阀门,主要用于管道执行结束后,从request对象中获取异常信息,并封装到response中以便将问题展现给访问者。
  • StandardHostValve:StandardHost中最后的阀门,主要用于从request中选择其context映射的Context容器StandardContext以及访问request中的Session以更新会话的最后访问时间。
  • StandardContextValve:StandardContext中的唯一阀门,主要作用是禁止任何对WEB-INF或META-INF目录下资源的重定向访问,对应用程序热部署功能的实现,从request中获得StandardWrapper。
  • StandardWrapperValve:StandardWrapper中的唯一阀门,主要作用包括调用StandardWrapper的loadServlet方法生成Servlet实例和调用ApplicationFilterFactory生成Filter链。
  有了以上对Tomcat的管道设计的讲述,我们下面详细剖析其实现。  在 《Tomcat7.0源码分析——请求原理分析(中)》一文中讲到执行管道的代码如代码清单1所示。

代码清单1

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

代码清单1中的getContainer方法获取到的实际是StandardService中的StandardEngine容器,根据《Tomcat7.0源码分析——生命周期管理》一文的内容,我们知道StandardEngine继承自ContainerBase,所以这里的getPipeline方法实际是ContainerBase实现的,代码如下:

    public Pipeline getPipeline() {

        return (this.pipeline);

    }

pipeline在ContainerBase实例化时生成,代码如下:

    protected Pipeline pipeline =
        CatalinaFactory.getFactory().createPipeline(this);

这里的CatalinaFactory采用单例模式实现,要获取CatalinaFactory实例,只能通过调用getFactory方法,见代码清单2。createPipeline方法中创建了StandardPipeline,StandardPipeline是Pipeline的标准实现。

代码清单2

public class CatalinaFactory {
    
    private static CatalinaFactory factory = new CatalinaFactory();
    
    public static CatalinaFactory getFactory() {
        return factory;
    }
    
    private CatalinaFactory() {
        // Hide the default constructor
    }
    
    public String getDefaultPipelineClassName() {
        return StandardPipeline.class.getName();
    }

    public Pipeline createPipeline(Container container) {
        Pipeline pipeline = new StandardPipeline();
        pipeline.setContainer(container);
        return pipeline;
    }
}

代码清单1随后调用了StandardPipeline的getFirst方法(见代码清单3)用来获取管道中的第一个Valve ,由于Tomcat并没有为StandardEngine的StandardPipeline设置first,因此将返回StandardPipeline的basic。

代码清单3

    public Valve getFirst() {
        if (first != null) {
            return first;
        }
        
        return basic;
    }

代码清单3中的basic的类型是StandardEngineValve,那么它是何时添加到StandardEngine的StandardPipeline中的呢?还记得《Tomcat7.0源码分析——server.xml文件的加载与解析》一文介绍过的ObjectCreateRule?在执行ObjectCreateRule的begin方法时,会反射调用StandardEngine的构造器生成StandardEngine的实例,StandardEngine的构造器中就会给其StandardPipeline设置basic为StandardEngineValve,见代码清单4。

代码清单4

    /**
     * Create a new StandardEngine component with the default basic Valve.
     */
    public StandardEngine() {

        super();
        pipeline.setBasic(new StandardEngineValve());
        /* Set the jmvRoute using the system property jvmRoute */
        try {
            setJvmRoute(System.getProperty("jvmRoute"));
        } catch(Exception ex) {
        }
        // By default, the engine will hold the reloading thread
        backgroundProcessorDelay = 10;

    }

代码清单1中最后调用了StandardEngineValve的invoke方法(见代码清单5)正式将请求交给管道处理。根据《Tomcat7.0源码分析——请求原理分析(中)》一文对postParseRequest方法的介绍,request已经被映射到相对应的Context容器(比如/manager)。此处首先调用request的getHost方法(实质是通过request映射的Context容器获取父容器得到,见代码清单6)获取Host容器,然后调用Host容器的Pipeline的getFirst方法获得AccessLogValve。AccessLogValve的invoke方法(见代码清单7),从中可以看出调用了getNext方法获取Host容器的Pipeline的下一个Valve,并调用其invoke方法。

代码清单5

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

        // Select the Host to be used for this Request
        Host host = request.getHost();
        if (host == null) {
            response.sendError
                (HttpServletResponse.SC_BAD_REQUEST,
                 sm.getString("standardEngine.noHost", 
                              request.getServerName()));
      
  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值