亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

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

2019-11-14 15:20:11
字體:
來源:轉載
供稿:網友

前言

  本文繼續講解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所示。

代碼清單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.49028c.com/jiaan-geng/p/4898871.html 

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品成人一区二区三区| 日韩视频免费看| 日韩在线视频观看正片免费网站| 亚洲精品wwwww| 人人爽久久涩噜噜噜网站| 国产日韩在线精品av| 久久久日本电影| 中文字幕亚洲色图| 日韩精品极品视频| 精品亚洲国产视频| 在线视频一区二区| 久久97久久97精品免视看| 日本久久91av| 国产国语刺激对白av不卡| 欧美日韩国产一中文字不卡| 国产精品久久在线观看| 亚洲第一页中文字幕| 欧美中文在线观看| 69**夜色精品国产69乱| 亚洲欧美国产精品专区久久| 久青草国产97香蕉在线视频| 亚洲影院色无极综合| 91高清在线免费观看| 午夜精品福利在线观看| 欧美性生交大片免费| 久久精品在线视频| 亚洲欧美日韩一区二区三区在线| 国产美女久久久| 国产精品久久久久久久久久久新郎| www.欧美免费| 日韩av网址在线观看| 国产在线精品一区免费香蕉| 成人做爰www免费看视频网站| 俺去啦;欧美日韩| 精品久久久久久久久久ntr影视| 亚洲免费小视频| 亚洲第一网站男人都懂| 97人人爽人人喊人人模波多| 欧美精品第一页在线播放| 亚洲激情中文字幕| 欧美黑人狂野猛交老妇| 亚洲精品一区二区网址| 91精品视频免费观看| 日韩av观看网址| 久久人人爽人人爽爽久久| 日韩精品福利在线| 欧美美最猛性xxxxxx| 欧美激情性做爰免费视频| 一区二区亚洲欧洲国产日韩| 九九热在线精品视频| 精品视频在线观看日韩| 久久久久久久久久久久久久久久久久av| 亚洲精品电影在线观看| 国产精品视频大全| 日韩有码片在线观看| 亚洲天堂第一页| 在线看日韩av| 亚洲男人7777| 久久久www成人免费精品| 亚洲桃花岛网站| 日韩国产欧美精品一区二区三区| 国产精品嫩草影院久久久| 成人精品在线观看| 国产69精品久久久久99| 色综合久久88色综合天天看泰| 久久精品国产一区二区三区| 日韩亚洲欧美中文高清在线| 亚洲精品一区二三区不卡| 久久精品成人动漫| 激情亚洲一区二区三区四区| 91在线中文字幕| 亚洲一区二区三区久久| 视频直播国产精品| 国产精品视频专区| 国产午夜精品麻豆| 国产成人一区二区三区电影| 一本色道久久88综合日韩精品| 91免费高清视频| 亚洲精品中文字幕av| 欧美电影免费观看网站| 国产精品久久久久久av福利| 欧美亚洲在线播放| 欧美日韩国产91| 亚洲精品国产suv| 亚洲一区二区三区成人在线视频精品| 成人黄色av免费在线观看| 日韩在线观看av| 欧美亚洲视频在线观看| 中文字幕亚洲欧美| 国产在线精品一区免费香蕉| 91在线观看免费高清完整版在线观看| 伊人久久男人天堂| 欧美日韩在线一区| 成人av.网址在线网站| 国产精品一区二区久久| 91免费视频网站| 亚洲男人天堂网站| 欧美日韩激情视频| 欧美与黑人午夜性猛交久久久| 欧美激情女人20p| 国产欧美va欧美va香蕉在线| 8x海外华人永久免费日韩内陆视频| 欧美视频中文字幕在线| 最近日韩中文字幕中文| 久久婷婷国产麻豆91天堂| 国产精品亚洲欧美导航| 欧美性猛交xxxx免费看久久久| 欧洲亚洲免费在线| 国产精品精品久久久久久| 久久精品视频在线| 亚洲免费视频网站| 欧美在线视频在线播放完整版免费观看| 国产精品久久久久久久久免费| 国产精品久久99久久| 欧美日韩亚洲一区二| 国产在线视频91| 亚洲男人天堂2024| 色悠悠国产精品| 最近2019免费中文字幕视频三| 欧美日韩加勒比精品一区| 欧美整片在线观看| 国产精品日韩在线播放| 午夜精品福利视频| 亚洲激情在线观看视频免费| 欧美精品国产精品日韩精品| 国产精品成人品| 亚洲男人天堂网| 亚洲第一精品久久忘忧草社区| 欧洲亚洲在线视频| 亚洲久久久久久久久久久| 国产+成+人+亚洲欧洲| 中文字幕日本精品| 欧美日韩福利视频| 久久精品亚洲94久久精品| 国产精品久久久久免费a∨| 91久久夜色精品国产网站| 国产婷婷97碰碰久久人人蜜臀| 国产精欧美一区二区三区| 亚洲国产黄色片| 91黄色8090| 日韩电影在线观看中文字幕| 日韩精品中文字幕有码专区| 亚洲一区制服诱惑| 日韩精品视频在线播放| 国产97在线|日韩| 91色精品视频在线| 欧洲精品毛片网站| 在线丨暗呦小u女国产精品| 亚洲在线免费观看| 欧美日韩国产丝袜另类| 日韩最新av在线| 91精品在线看| 亚洲成人免费网站| 久久青草精品视频免费观看| 亚洲人成网站免费播放| 午夜精品一区二区三区在线播放| 富二代精品短视频| 欧美性猛交xxxx乱大交3| 亚洲精品久久久久久下一站| 久久亚洲精品一区| 亚洲成av人乱码色午夜| 亚洲午夜未删减在线观看| 国产精品久久久久久久7电影| 黑人巨大精品欧美一区二区三区|