Tomcat源码分析——请求原理分析(下)

前言

  本文继续讲解TOMCAT的请求原理分析,建议朋友们阅读本文时首先阅读过《TOMCAT源码分析——请求原理分析(上)》和《TOMCAT源码分析——请求原理分析(中)》。在《TOMCAT源码分析——请求原理分析(中)》一文我简单讲到了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接口在《TOMCAT源码分析——生命周期管理》一文详细阐述)进行介绍:

  • 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的管道设计的讲述,我们下面详细剖析其实现。

  在《TOMCAT源码分析——请求原理分析(中)》一文中讲到执行管道的代码如代码清单1所示。

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

代码清单1中的getContainer方法获取到的实际是StandardService中的StandardEngine容器,根据《TOMCAT源码分析——生命周期管理》一文的内容,我们知道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中的呢?还记得《TOMCAT源码分析——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)正式将请求交给管道处理。根据《TOMCAT源码分析——请求原理分析(中)》一文对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()));
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);

    }

代码清单6

    public Host getHost() {
        if (getContext() == null)
            return null;
        return (Host) getContext().getParent();
        //return ((Host) mappingData.host);
    }

代码清单7

    @Override
    public void invoke(Request request, Response response) throws IOException,
            ServletException {
        final String t1Name = AccessLogValve.class.getName()+".t1";
        if (getState().isAvailable() && getEnabled()) {
            // Pass this request on to the next valve in our pipeline
            long t1 = System.currentTimeMillis();
            boolean asyncdispatch = request.isAsyncDispatching();
            if (!asyncdispatch) {
                request.setAttribute(t1Name, new Long(t1));
            }

            getNext().invoke(request, response);

            //we‘re not done with the request
            if (request.isAsyncDispatching()) {
                return;
            } else if (asyncdispatch && request.getAttribute(t1Name)!=null) {
                t1 = ((Long)request.getAttribute(t1Name)).longValue();
            }

            long t2 = System.currentTimeMillis();
            long time = t2 - t1;

            log(request,response, time);
        } else
            getNext().invoke(request, response);
    }

根据以上分析,我们看到StandardEngine容器的Pipeline中只有一个Valve(StandardEngineValve),而StandardHost容器中有三个Valve(分别是AccessLogValve、ErrorReportValve和StandardHostValve),此外StandardContext容器中有一个Valve(StandardContextValve),StandardWrapper中也只有一个Valve(StandardWrapperValve)。这些阀门Valve通过invoke方法彼此串联起来,最终构成的执行顺序十分类似于一个管道,最终形成的管道正如图2一样,这也许是Pipeline名字的由来。

图2  Tomcat管道示意图

本文以StandardEngineValve和AccessLogValve为例讲了Valve的实现,以及Pipeline是如何串联起来的,我们最后看看StandardWrapperValve的实现,其它Valve的实现不再赘述。

Filter与职责链模式

  根据对管道和阀门的分析, 我们知道要分析StandardWrapperValve,只需直接阅读其invoke方法即可,见代码清单8所示。

代码清单8

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

        // Initialize local variables we may need
        boolean unavailable = false;
        Throwable throwable = null;
        // This should be a Request attribute...
        long t1=System.currentTimeMillis();
        requestCount++;
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();

        // 省略校验及次要代码

        // Allocate a servlet instance to process this request
        try {
            if (!unavailable) {
                servlet = wrapper.allocate();
            }
        } catch (UnavailableException e) {
            // 省略异常处理代码
        } catch (ServletException e) {
            // 省略异常处理代码
        } catch (Throwable e) {
            // 省略异常处理代码
        }

        // Identify if the request is Comet related now that the servlet has been allocated
        boolean comet = false;
        if (servlet instanceof CometProcessor
                && request.getAttribute("org.apache.tomcat.comet.support") == Boolean.TRUE) {
            comet = true;
            request.setComet(true);
        }

        // Acknowledge the request
        try {
            response.sendAcknowledgement();
        } catch (IOException e) {
            // 省略异常处理代码
        } catch (Throwable e) {
            // 省略异常处理代码
        }
        MessageBytes requestPathMB = request.getRequestPathMB();
        DispatcherType dispatcherType = DispatcherType.REQUEST;
        if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
        request.setAttribute
            (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
             dispatcherType);
        request.setAttribute
            (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
             requestPathMB);
        // Create the filter chain for this request
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);

        // Reset comet flag value after creating the filter chain
        request.setComet(false);

        // Call the filter chain for this request
        // NOTE: This also calls the servlet‘s service() method
        try {
            String jspFile = wrapper.getJspFile();
            if (jspFile != null)
                request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
            else
                request.removeAttribute(Globals.JSP_FILE_ATTR);
            if ((servlet != null) && (filterChain != null)) {
                // Swallow output if needed
                if (context.getSwallowOutput()) {
                    try {
                        SystemLogHandler.startCapture();
                        if (request.isAsyncDispatching()) {
                            //TODO SERVLET3 - async
                            ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
                        } else if (comet) {
                            filterChain.doFilterEvent(request.getEvent());
                            request.setComet(true);
                        } else {
                            filterChain.doFilter(request.getRequest(),
                                    response.getResponse());
                        }
                    } finally {
                        String log = SystemLogHandler.stopCapture();
                        if (log != null && log.length() > 0) {
                            context.getLogger().info(log);
                        }
                    }
                } else {
                    if (request.isAsyncDispatching()) {
                        //TODO SERVLET3 - async
                        ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
                    } else if (comet) {
                        request.setComet(true);
                        filterChain.doFilterEvent(request.getEvent());
                    } else {
                        filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                    }
                }

            }
            request.removeAttribute(Globals.JSP_FILE_ATTR);
        } catch (ClientAbortException e) {
            // 省略异常处理代码
        } catch (IOException e) {
            // 省略异常处理代码
        } catch (UnavailableException e) {
            // 省略异常处理代码
        } catch (ServletException e) {
            // 省略异常处理代码
        } catch (Throwable e) {
            // 省略异常处理代码
        }

        // Release the filter chain (if any) for this request
        if (filterChain != null) {
            if (request.isComet()) {
                // If this is a Comet request, then the same chain will be used for the
                // processing of all subsequent events.
                filterChain.reuse();
            } else {
                filterChain.release();
            }
        }

        // Deallocate the allocated servlet instance
        try {
            if (servlet != null) {
                wrapper.deallocate(servlet);
            }
        } catch (Throwable e) {
            // 省略异常处理代码
            }
        }

        // If this servlet has been marked permanently unavailable,
        // unload it and release this instance
        try {
            if ((servlet != null) &&
                (wrapper.getAvailable() == Long.MAX_VALUE)) {
                wrapper.unload();
            }
        } catch (Throwable e) {
            // 省略异常处理代码
        }
        long t2=System.currentTimeMillis();

        long time=t2-t1;
        processingTime += time;
        if( time > maxTime) maxTime=time;
        if( time < minTime) minTime=time;

    }

通过阅读代码清单8,我们知道StandardWrapperValve的invoke方法的执行步骤如下:

  1. 调用StandardWrapper的allocate方法分配org.apache.catalina.servlets.DefaultServlet的实例处理访问包括*.html、*.htm、*.gif、*.jpg、*.jpeg等资源的request,分配org.apache.jasper.servlet.JspServlet的实例处理访问*.jpg页面的request。简单提下这些Servlet实例是在StandardContext启动的时候调用StandardWrapper的load方法用反射生成的,有关StandardContext启动的内容可以参考《TOMCAT源码分析——生命周期管理》一文。
  2. 确认当前request是否是Comet的,由于默认的DefaultServlet并未实现CometProcessor接口,所以不会作为Comet的请求处理。顺便简单提下,Comet 指的是一种 Web 应用程序的架构。在这种架构中,客户端程序(通常是浏览器)不需要显式的向服务器端发出请求,服务器端会在其数据发生变化的时候主动的将数据异步的发送给客户端,从而使得客户端能够及时的更新用户界面以反映服务器端数据的变化。
  3. 向客户端发送确认。
  4. 给request对象设置请求类型和请求路径属性。
  5. 获取ApplicationFilterFactory(单例模式实现),并调用其createFilterChain方法创建ApplicationFilterChain。
  6. 调用ApplicationFilterChain的doFilter方法,执行ApplicationFilterChain中维护的Filter职责链。
  7. 调用ApplicationFilterChain的release方法清空对Servlet、Filter的引用。
  8. 调用StandardWrapper的deallocate方法释放为其分配的Servlet。

注意:如果接收请求的Servlet实现了SingleThreadModel接口,那么singleThreadModel属性为true,则Tomcat的StandardWrapper中只有一个Servlet实例,否则会创建一个Servlet实例池。

创建Filter职责链用到createFilterChain方法,其实现见代码清单9。

代码清单9

    public ApplicationFilterChain createFilterChain
        (ServletRequest request, Wrapper wrapper, Servlet servlet) {

        // get the dispatcher type
        DispatcherType dispatcher = null;
        if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) {
            dispatcher = (DispatcherType) request.getAttribute(DISPATCHER_TYPE_ATTR);
        }
        String requestPath = null;
        Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR);

        if (attribute != null){
            requestPath = attribute.toString();
        }

        // If there is no servlet to execute, return null
        if (servlet == null)
            return (null);

        boolean comet = false;

        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            comet = req.isComet();
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
                if (comet) {
                    req.setFilterChain(filterChain);
                }
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);

        filterChain.setSupport
            (((StandardWrapper)wrapper).getInstanceSupport());

        // Acquire the filter mappings for this Context
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();

        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return (filterChain);

        // Acquire the information we will need to match filter mappings
        String servletName = wrapper.getName();

        // Add the relevant path-mapped filters to this filter chain
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            boolean isCometFilter = false;
            if (comet) {
                try {
                    isCometFilter = filterConfig.getFilter() instanceof CometFilter;
                } catch (Exception e) {
                    // Note: The try catch is there because getFilter has a lot of
                    // declared exceptions. However, the filter is allocated much
                    // earlier
                }
                if (isCometFilter) {
                    filterChain.addFilter(filterConfig);
                }
            } else {
                filterChain.addFilter(filterConfig);
            }
        }

        // Add filters that match on servlet name second
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            boolean isCometFilter = false;
            if (comet) {
                try {
                    isCometFilter = filterConfig.getFilter() instanceof CometFilter;
                } catch (Exception e) {
                    // Note: The try catch is there because getFilter has a lot of
                    // declared exceptions. However, the filter is allocated much
                    // earlier
                }
                if (isCometFilter) {
                    filterChain.addFilter(filterConfig);
                }
            } else {
                filterChain.addFilter(filterConfig);
            }
        }

        // Return the completed filter chain
        return (filterChain);

    }

根据代码清单9,我们整理下整个创建Filter职责链的过程:

  1. 从request中获取请求的类型(Tomcat目前提供的请求类型有REQUEST、FORWARD、INCLUDE、ASYNC及ERROR五种)与路径;
  2. 创建ApplicationFilterChain并设置给当前request;
  3. 给ApplicationFilterChain设置Servlet,即DefaultServlet;
  4. 从StandardContext中获取当前Context的filterMaps;
  5. 如果filterMaps为空,则说明当前Context没有配置Filter,否则会将filterMaps中的Filter全部添加到ApplicationFilterChain中的Filter职责链中。

  调用ApplicationFilterChain的doFilter方法,执行ApplicationFilterChain中维护的Filter职责链。Filter职责链是对职责链模式的经典应用,我们先通过图3来介绍其执行流程。

图3  Tomcat的Filter职责链执行流程

这里对图3的执行过程进行介绍:

  1. StandardWrapperValve的invoke方法在创建完ApplicationFilterChain后,第一次调用ApplicationFilterChain的doFilter方法;
  2. 如果ApplicationFilterChain自身维护的Filter数组中还有没有执行的Filter,则取出此Filter并执行Filter的doFilter方法(即第3步),否则执行Servlet的service方法处理请求(即第4步);
  3. 每个Filter首先执行自身的过滤功能,最后在执行结束前会回调ApplicationFilterChain的doFilter方法,此时会将执行流程交给第2步;
  4. Servlet的service实际会调用自身的doGet、doHead、doPost、doPut、doDelete等方法。

第2步对应了图3中M.N这个标记的M部分,第3步则对应N的部分。

本文最后从源码实现级别分析Filter职责链的执行过程,首先来看ApplicationFilterChain的doFilter方法,见代码清单10。

代码清单10

    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

        if( Globals.IS_SECURITY_ENABLED ) {
            final ServletRequest req = request;
            final ServletResponse res = response;
            try {
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedExceptionAction<Void>() {
                        public Void run()
                            throws ServletException, IOException {
                            internalDoFilter(req,res);
                            return null;
                        }
                    }
                );
            } catch( PrivilegedActionException pe) {
                Exception e = pe.getException();
                if (e instanceof ServletException)
                    throw (ServletException) e;
                else if (e instanceof IOException)
                    throw (IOException) e;
                else if (e instanceof RuntimeException)
                    throw (RuntimeException) e;
                else
                    throw new ServletException(e.getMessage(), e);
            }
        } else {
            internalDoFilter(request,response);
        }
    }

从代码清单10看到ApplicationFilterChain的doFilter方法主要调用了internalDoFilter方法(见代码清单11)。

代码清单11

    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            try {
                filter = filterConfig.getFilter();
                support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
                                          filter, request, response);

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                            Boolean.FALSE);
                }
                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                        ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege
                        ("doFilter", filter, classType, args, principal);

                    args = null;
                } else {
                    filter.doFilter(request, response, this);
                }

                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                          filter, request, response);
            } catch (IOException e) {
                if (filter != null)
                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                              filter, request, response, e);
                throw e;
            } catch (ServletException e) {
                if (filter != null)
                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                              filter, request, response, e);
                throw e;
            } catch (RuntimeException e) {
                if (filter != null)
                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                              filter, request, response, e);
                throw e;
            } catch (Throwable e) {
                if (filter != null)
                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                              filter, request, response, e);
                throw new ServletException
                  (sm.getString("filterChain.filter"), e);
            }
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        try {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(request);
                lastServicedResponse.set(response);
            }

            support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
                                      servlet, request, response);
            if (request.isAsyncSupported()
                    && !support.getWrapper().isAsyncSupported()) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                        Boolean.FALSE);
            }
            // Use potentially wrapped request from this point
            if ((request instanceof HttpServletRequest) &&
                (response instanceof HttpServletResponse)) {

                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                        ((HttpServletRequest) req).getUserPrincipal();
                    Object[] args = new Object[]{req, res};
                    SecurityUtil.doAsPrivilege("service",
                                               servlet,
                                               classTypeUsedInService,
                                               args,
                                               principal);
                    args = null;
                } else {
                    servlet.service(request, response);
                }
            } else {
                servlet.service(request, response);
            }
            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                      servlet, request, response);
        } catch (IOException e) {
            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                      servlet, request, response, e);
            throw e;
        } catch (ServletException e) {
            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                      servlet, request, response, e);
            throw e;
        } catch (RuntimeException e) {
            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                      servlet, request, response, e);
            throw e;
        } catch (Throwable e) {
            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                      servlet, request, response, e);
            throw new ServletException
              (sm.getString("filterChain.servlet"), e);
        } finally {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(null);
                lastServicedResponse.set(null);
            }
        }

    }

执行Servlet

从代码清单11,我们可以看到ApplicationFilterChain最后会执行Servlet的service方法,此service方法实际是所有Servlet的父类HttpServlet实现的,见代码清单12。

代码清单12

    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
    }
}

代码清单12中的service方法调用重载的service方法,后者通过判断HttpServletRequest对象的HTTP Method,调用不同的方法,如GET、DELETE、POST等,见代码清单13。

代码清单13

    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn‘t support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);        

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

以doGet方法为例,见代码清单14。

代码清单14

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }

不对啊!为什么doGet方法的实现只是返回了400和405错误呢?因为这是抽象类HttpServlet的默认实现,用户必须实现自身的Servlet或者使用默认的DefaultServlet。

至此,Tomcat有关请求流程的主要内容已经讲解完毕。欢迎大家提出宝贵意见!

如需转载,请标明本文作者及出处——作者:jiaan.gja,本文原创首发:博客园,原文链接:http://www.cnblogs.com/jiaan-geng/p/4898871.html 
时间: 2024-10-03 15:01:51

Tomcat源码分析——请求原理分析(下)的相关文章

Tomcat源码分析——请求原理分析(中)

前言 在<TOMCAT源码分析——请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT源码分析——请求原理分析(上)>一文中的相关知识以及HTTP协议和TCP协议的一些内容.本文重点讲解Tomcat7.0在准备好接受请求后,请求过程的原理分析. 请求处理架构 在正式开始之前,我们先来看看图1中的Tomcat请求处理架构. 图1 Tomcat请求处理架构 图1列出了Tomcat请求处理架构中的主要组件,这里

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

前言 在<TOMCAT7.0源码分析--请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT7.0源码分析--请求原理分析(上)>一文中的相关知识以及HTTP协议和TCP协议的一些内容.本文重点讲解Tomcat7.0在准备好接受请求后,请求过程的原理分析. 请求处理架构 在正式开始之前,我们先来看看图1中的Tomcat请求处理架构. 图1 Tomcat请求处理架构 图1列出了Tomcat请求处理架构中的主

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

前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程序员自己实现Jsp页面或者Servlet接受请求,后来借助Struts1.Struts2.Spring等中间件后,实际也是利用Filter或者Servlet处理请求,大家肯定要问了,这些Servlet处理的请求来自哪里?Tomcat作为Web服务器是怎样将HTTP请求交给Servlet的呢? 本文就

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

Tomcat7.0源码分析--请求原理分析 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程序员自己实现Jsp页面或者Servlet接受请求,后来借助Struts1.Struts2.spring等中间件后,实际也是利用Filter或者Servlet处理请求,大家肯定要问了,这些Servlet处理的请求来自哪里?Tomcat作为Web服务器是怎样将HTT

Tomcat 源码分析(转)

Tomcat源码分析(一)--服务启动 1. Tomcat主要有两个组件,连接器和容器,所谓连接器就是一个http请求过来了,连接器负责接收这个请求,然后转发给容器.容器即servlet容器,容器有很多层,分别是Engine,     Host,Context,Wrapper.最大的容器Engine,代表一个servlet引擎,接下来是Host,代表一个虚拟机,然后是Context,代表一个应用,Wrapper对应一个servlet.从连接器     传过来连接后,容器便会顺序经过上面的容器,最

tomcat源码分析前的准备工作

Tomcat源码学习前的准备工作 注:由于网上的帖子大部分没有配套的图片和错误的分析,所有费了半天劲整理了此篇博客,希望大家少走弯路吧 下面我们就开始我们的Tomcat源码学习之旅. 1. 下载Tomcat6.0的源代码 首先,我们得下载Tomcat6.0的源代码.Tomcat源代码的版本控制工具不是CVS,而是Subversion,如果您的机器上没有安装Subversion,请从http://subversion.tigris.org/servlets/ProjectDocumentList?

tomcat源码分析(一)从tomcat架构说起

p { margin-bottom: 0.25cm; line-height: 120% } 首先dowload源码并导入到Eclipse中,导入后代码代码的层次如下图所示.先简单来看下tomcat源码的结构.javax这个包主要是有关JavaEE规范的,比如Servlet等等,并不是我们主要分析的.需要分析有org.apache下的子包,其中catalina中很重要,其中tomcat的启动就在该包下中的startup包下, catalina下还定义了tomcat容器(server,servic

Tomcat源码分析——Session管理分析(上)

前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对于使用Tomcat作为Web容器的大部分开发人员而言,Tomcat是如何实现Session标记用户和管理Session信息的呢? 概要 Session Tomcat内部定义了Session和HttpSession这两个会话相关的接口,其类继承体系如图1所示. 图1 Session类继承体系 图1中额

Tomcat源码分析

前言: 本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动过程. 如果有不明白的地方, 再来查阅本文, 看是否能得到帮助. 我相信这样效果以及学习速度都会好很多! 1. Tomcat的整体框架结构 Tomcat的基本框架,