Tomcat源碼分析——請求原理分析(下)

前言

  本文繼續講解TOMCAT的請求原理分析,建議朋友們閱讀本文時首先閱讀過《TOMCAT源碼分析——請求原理分析(上)》和《TOMCAT源碼分析——請求原理分析(中)》。在《TOMCAT源碼分析——請求原理分析(中)》一文我簡單講到了Pipeline,但並未徹底展開,本文將從Pipeline開始講解請求原理的剩餘內容。html

管道

  在Tomcat中管道Pipeline是一個接口,定義了使得一組閥門Valve按照順序執行的規範,Pipeline中定義的接口以下:java

  • getBasic:獲取管道的基礎閥門;
  • setBasic:設置管道的基礎閥門;
  • addValve:添加閥門;
  • getValves:獲取閥門集合;
  • removeValve:移除閥門;
  • getFirst:獲取第一個閥門;
  • isAsyncSupported:當管道中的全部閥門都支持異步時返回ture,不然返回false;
  • getContainer:獲取管道相關聯的容器,好比StandardEngine;
  • setContainer:設置管道相關聯的容器。

  Engine、Host、Context及Wrapper等容器都定義了自身的Pipeline,每一個Pipeline都包含一到多個Valve。Valve定義了各個閥門的接口規範,其類繼承體系如圖1所示。apache

圖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

  在《TOMCAT源碼分析——請求原理分析(中)》一文中講到執行管道的代碼如代碼清單1所示。服務器

代碼清單1架構

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

代碼清單1中的getContainer方法獲取到的實際是StandardService中的StandardEngine容器,根據《TOMCAT源碼分析——生命週期管理》一文的內容,咱們知道StandardEngine繼承自ContainerBase,因此這裏的getPipeline方法實際是ContainerBase實現的,代碼以下:app

    public Pipeline getPipeline() {

        return (this.pipeline);

    }

pipeline在ContainerBase實例化時生成,代碼以下:less

    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 
相關文章
相關標籤/搜索