spring源碼------一個請求在spring中的處理過程(從FrameworkServlet規(guī)范到DispatcherServlet)代碼及流程圖說明 (2)

?前面的文章已經(jīng)講了從一個請求在spring中的處理過程(從Servlet規(guī)范到FrameworkServlet)代碼及流程圖說明接下來就是,從FrameworkServletDispatcherServlet的部分進(jìn)行分析了。

1.從FrameworkServletDispatcherServlet

?在FrameworkServletprocessRequest方法中,有調(diào)用doService方法的這個步驟。這個方法是一個抽象方法,必須由子類來實現(xiàn)。這個方法是處理GET, POST, PUT , DELETE請求具體邏輯的方法。

1.1 從FrameworkServlet進(jìn)入到DispatcherServletprocessRequest

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
            ......
            //進(jìn)行業(yè)務(wù)處理
            doService(request, response);
            ......
}

?所以我們可以直接到DispatcherServlet中進(jìn)一步的分析。直接看對應(yīng)的doService方法

1.2 進(jìn)行真正請求處理前的準(zhǔn)備doService

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logRequest(request);

        // Keep a snapshot of the request attributes in case of an include,  to be able to restore the original attributes after the include.
        //如果是一個include請求,<jsp:incluede page="xxx.jsp"/> 這種中,可能在一個請求(A)中嵌套了另外的一個請求(B),因此需要備份當(dāng)前請求(A)
        Map<String, Object> attributesSnapshot = null;
        //是否是一個include請求,通過request中的javax.servlet.include.request_uri屬性判斷,JSP在運行期間是會被編譯成相應(yīng)的Servlet類來運行的,
        // 所以在Servlet中也會有類似的功能和調(diào)用語法,這就是RequestDispatch.include()方法,在一個被別的servlet使用RequestDispatcher的include方法調(diào)用過的servlet中,
        // 如果它想知道那個調(diào)用它的servlet的上下文信息該怎么辦呢,那就可以通過request中的attribute中的如下屬性獲?。簀avax.servlet.include.request_uri
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                //獲取包含的請求中的獲取A請求的內(nèi)部B請求設(shè)定的spring的策略
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }
        // Make framework objects available to handlers and view objects.
        //設(shè)置web應(yīng)用上下文到請求中,
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        //設(shè)置本地解析器
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        //設(shè)置主題解析器
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        //設(shè)置主題,如果沒有設(shè)置則為null,默認(rèn)的為WebApplicationContext
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
        //將request跟response保存到一個FlashMap中,F(xiàn)lashMap用來將一個請求跟另外一個請求關(guān)聯(lián)起來,通常在redirect的時候有用
        if (this.flashMapManager != null) {
            //如果當(dāng)前請求的FlashMap在之前的請求中保存過了,則取出來,并去除對應(yīng)的緩存
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            //保存FlashMap到屬性中
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        try {
            //進(jìn)行請求分發(fā)處理
            doDispatch(request, response);
        }
        finally {
            //在FrameworkServlet中會生成WebAsyncManager
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

?這里對上面的流程總結(jié)一下:

  1. 檢查當(dāng)前的請求中是否包含另外的一個請求(也就是請求包含),如果是這種情況需要把內(nèi)部請求的屬性作為快照保存起來。(個人對于這一塊還是有點沒有理解清楚的,歡迎指點)
  2. 設(shè)置對應(yīng)的web請求上下,本地解析器,主題解析器,主題到request中
  3. 檢查flashMapManager是不是null(這個不會為空,在initFlashMapManager方法初始化的時候回創(chuàng)建),如果不是空則吧對應(yīng)的request, respons設(shè)置進(jìn)一個FlashMap對象中,同時設(shè)置對應(yīng)的輸入,輸出跟flashMap管理對象到request
  4. 進(jìn)行請求分發(fā)處理doDispatch方法
  5. 最后處理異步請求處理相關(guān)的邏輯,主要檢查這個請求的異步處理邏輯是不是正在處理,是的就將上面保存的內(nèi)部請求設(shè)置到request中。

?這里涉及到請求包含(include)跟請求轉(zhuǎn)發(fā)(forward)。關(guān)于這兩個東西可以百度一下或者參考下面這個文章請求包含(Include)和請求轉(zhuǎn)發(fā)(Forward)這個位置理解有點費力。個人理解可能也有點缺陷,歡迎指正。

?在準(zhǔn)備好了處理請求需要的相關(guān)環(huán)境跟參數(shù)的之后,就是進(jìn)行請求的分發(fā)處理了,之所以叫分發(fā)是因為每個請求都有對應(yīng)的處理類,現(xiàn)在進(jìn)入到分發(fā)的邏輯方法doDispatch

1.3 進(jìn)行請求分發(fā)的doDispatch

?因為doDispatch這個方法的內(nèi)部邏輯比較復(fù)雜(方法內(nèi)部調(diào)用了其他的復(fù)雜的方法邏輯),一篇博客肯定寫不完,所以這里主要寫整個方法內(nèi)的邏輯。

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        //從request中獲取WebAsyncManager
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                //檢查是不是multipart請求并將請求轉(zhuǎn)化為MultipartHttpServletRequest類型的
                processedRequest = checkMultipart(request);
                //如果請求不是原來的request請求,則表示是multipart請求并且解析過的
                multipartRequestParsed = (processedRequest != request);

                //獲取封裝了HandlerInterceptor的HandlerExecutionChain
                mappedHandler = getHandler(processedRequest);
                //如果不存在對應(yīng)的處理鏈則返回
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                //從HandlerExecutionChain中獲取Handler然后尋找合適的HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                //獲取請求方式
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                //檢查是不是GET請求
                if (isGet || "HEAD".equals(method)) {
                    //檢查當(dāng)前的get類型的請求的最后修改時間是不是存在的,這個參數(shù)用來減少數(shù)據(jù)傳輸用
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    //如果瀏覽器請求中的最后請求時間跟服務(wù)器的最后修改時間一致,并且是get類型請求則直接返回。關(guān)于lastModified這個會說明
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                //調(diào)用mappedHandler中攔截器的applyPreHandle方法,如果對應(yīng)的請求不符合規(guī)則則直接返回
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                //執(zhí)行對應(yīng)的HandlerAdapter的Handler方法,拿到對應(yīng)的視圖
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                //檢查當(dāng)前的請求是否正在異步處理,如果是的則直接放棄并返回
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                //如果處理的結(jié)果返回的視圖是空的則使用默認(rèn)的視圖,不為空則用處理的結(jié)果
                applyDefaultViewName(processedRequest, mv);
                //調(diào)用applyPostHandle方法對視圖進(jìn)行返回后的處理
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                //進(jìn)行錯誤視圖的處理
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            //對正常視圖或者錯誤視圖的處理
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            //檢查當(dāng)前的請求是否正在異步處理
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                //如果mappedHandler不是null,則調(diào)用對應(yīng)的mappedHandler中的AsyncHandlerInterceptor
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                //對流類型的請求,做后置的處理
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

?對于doDispatch方法的處理流程就跟網(wǎng)上的mvc處理流程圖一樣的,把整個流程包含在了一個方法里面。這里就用網(wǎng)上的一張圖具體說明

流程圖

  1. 首先會檢查當(dāng)前的請求是不是multipart/form-data類型的請求,是的則將請求進(jìn)行轉(zhuǎn)換為MultipartHttpServletRequest類型的request。(MultipartHttpServletRequestHttpServletRequest的子類)
  2. 獲取合適的獲取封裝了HandlerInterceptor集合的HandlerExecutionChain,如果沒有找到合適的,就直接返回404的錯誤頁面
  3. HandlerExecutionChain中獲取Handler然后尋找合適的HandlerAdapter
  4. 檢查是不是get請求,是的然后在檢查lastModified這個屬性選擇是直接返回還是進(jìn)行后續(xù)的請求處理。(lastModified這個屬性后面說一下,其實前面文章已經(jīng)說過了)
  5. 調(diào)用前面選擇的HandlerAdapterapplyPreHandle方法檢查請求是否符合定義的要求,不符合就返回。
  6. 調(diào)用前面選擇的HandlerAdapterhandle方法,進(jìn)行邏輯的處理,然后返回ModelAndView對象
  7. 檢查當(dāng)前的請求是否正在異步處理,如果是的則直接放棄并返回
  8. 檢查返回的ModelAndView的視圖是不是null,是的則返回默認(rèn)的,不是的則不處理
  9. 調(diào)用前面選擇的HandlerAdapterapplyPostHandle方法對視圖進(jìn)行最后的處理
  10. 對正常返回的或者發(fā)生異常時生成的視圖進(jìn)行處理,
  11. 對異步請求或multipart/form-data類型請求進(jìn)行后續(xù)的處理

?對于lastModified這個屬性可以參考這篇文章HttpServlet---getLastModified與緩存

后續(xù)的

?雖然整個的request請求的邏輯只有這么多,但是里面的每個步驟都是比較復(fù)雜的。這一篇講不完,因此挑選里面比較重要的幾個步驟進(jìn)行分篇講解。主要以下幾個步驟

  1. HandlerExecutionChain的選擇
  2. HandlerAdapter的獲取跟handle方法邏輯
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容