Spring MVC 源碼閱讀-請(qǐng)求的處理流程

本文內(nèi)容假設(shè)閱讀者對(duì)Spring MVC 源碼閱讀-框架啟動(dòng)過程中包含的知識(shí)點(diǎn)已經(jīng)有所了解。
本文的配置也是基于xml形式進(jìn)行的岩遗。

文章中的一些稱呼的說明
1.在Controller中創(chuàng)建的@RequestMapping在下文中稱為路徑映射
2.在Controller中創(chuàng)建的路徑映射方法在下文中稱為請(qǐng)求處理方法

@GetMapping("/a")     // 路徑映射
public String a() {   // 請(qǐng)求處理方法
    return ""; 
}

關(guān)于框架啟動(dòng)過程的文章中捧请,已經(jīng)指出Spring MVC是基于DispatcherServlet展開的,而DispatcherServlet又繼承自GenericServlet锅必,
也就是說Servlet的生命周期代表了Spring MVC的生命周期。
其中init(ServletConfig)代表了Spring MVC的啟動(dòng),那么service(ServletRequest, ServletResponse)也就應(yīng)該代表Spring MVC的請(qǐng)求的處理流程秉犹。

1. Spring MVC 請(qǐng)求處理流程

DispatcherServlet向上尋找service(ServletRequest, ServletResponse),最終會(huì)在HttpServlet中找到該方法稚晚。

// HttpServlet
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    HttpServletRequest  request;
    HttpServletResponse response;
    
    if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response");
    }

    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;

    // 這里的service方法也需要從DispatcherServlet開始自下向上查找崇堵,
    // 可以發(fā)現(xiàn)最早出現(xiàn)該方法的是FrameworkServlet,那這個(gè)方法調(diào)用的就是FrameworkServlet里面的
    service(request, response);
}

簡單的對(duì) ServletRequestServletResponse 進(jìn)行了類型轉(zhuǎn)換客燕,隨后調(diào)用 service(HttpServletRequest, HttpServletResponse) 方法

1.1. FrameworkServlet#service(HttpServletRequest, HttpServletResponse)

當(dāng)請(qǐng)求剛到來時(shí)鸳劳,第一步就是確認(rèn)請(qǐng)求方式,以便使用特定的方式對(duì)請(qǐng)求進(jìn)行業(yè)務(wù)處理也搓。
HttpServlet#service(HttpServletRequest, HttpServletResponse)中提供了針對(duì)GET赏廓、HEADERPOST傍妒、PUT幔摸、DELETEOPTIONS颤练、TRACE7種請(qǐng)求方式的處理抚太。
FrameworkServlet#service(HttpServletRequest, HttpServletResponse)中提供了PATCH請(qǐng)求方式的處理。
由于方法太多昔案,在下文中將圍繞POST請(qǐng)求方式展開邏輯描述尿贫。

// FrameworkServlet
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
        processRequest(request, response);
    }
    else {
        super.service(request, response);
    }
}

// HttpServlet
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();

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

    } else if (method.equals(METHOD_POST)) {
        // 這里的doPost方法也需要從DispatcherServlet開始自下向上查找,F(xiàn)rameworkServlet類中最早出現(xiàn)該方法
        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);
    }
}

// FrameworkServlet
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // 不管是doPost踏揣、doGet還是doDelete等庆亡,它們都要調(diào)用了這個(gè)方法
    processRequest(request, response);
}

1.2 FrameworkServlet#processRequest(HttpServletRequest, HttpServletResponse)

processRequest方法中做了這么幾件事情
1.HttpServletRequestHttpServletResponse 對(duì)象 以及 從HttpServletRequest中解析出來的 Locale 存放到線程本地內(nèi)。
2.根據(jù)當(dāng)前的請(qǐng)求創(chuàng)建一個(gè)異步管理程序來支持對(duì)異步返回值的處理捞稿。
3.調(diào)用請(qǐng)求處理的方法(doService(request, response);)又谋。
4.進(jìn)行收尾工作
~ 清除本次請(qǐng)求產(chǎn)生的數(shù)據(jù)
~ 調(diào)用注冊(cè)的銷毀邏輯
~ 對(duì)Session的內(nèi)容進(jìn)行一些操作
~ 向容器內(nèi)發(fā)送一個(gè) ServletRequestHandledEvent 類型的事件

// FrameworkServlet
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;

    // ---------------------------------------------------- 獲取 Locale ---------------------------------------------------

    // 從 ThreadLocal 中獲取與當(dāng)前線程相關(guān)聯(lián)的 LocaleContext,LocaleContext 里面存放的是一個(gè) Locale 對(duì)象娱局。
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    // buildLocaleContext方法彰亥,也要從 DispatcherServlet 類向上找,最早在 DispatcherServlet 中找到該方法衰齐。
    // 默認(rèn)情況下任斋,會(huì)使用到在Spring MVC框架啟動(dòng)過程中初始化的 LocaleResolver 來解析請(qǐng)求中的 Locale 對(duì)象
    LocaleContext localeContext = buildLocaleContext(request);

    // ---------------------------------------------- 獲取 RequestAttributes -----------------------------------------------

    // 從 ThreadLocal 中獲取與當(dāng)前線程相關(guān)聯(lián)的 RequestAttributes,RequestAttributes 內(nèi)存儲(chǔ)的是 request耻涛、response等內(nèi)容
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    // 通過創(chuàng)建 ServletRequestAttributes對(duì)象實(shí)例 來 存儲(chǔ)Request和Response废酷。
    // 除此之外瘟檩,ServletRequestAttributes 還提供了 對(duì)Request和Session屬性的查找與修改,使用見之后的分析澈蟆。
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    // ------------------------------------------ Spring MVC 對(duì)異步請(qǐng)求的支持 ------------------------------------------

    // 創(chuàng)建 WebAsyncManager 對(duì)象墨辛,并放到 request 中
    // 如果當(dāng)前請(qǐng)求是因?yàn)?AsyncContext#dispatch() 方法的調(diào)用而產(chǎn)生,那么這個(gè)請(qǐng)求內(nèi)部已經(jīng)被Servlet容器設(shè)置了這個(gè)值趴俘。
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    // 使用指定的key注冊(cè)一個(gè)攔截器睹簇,在 異步返回值(例如:Callable) 被執(zhí)行前后會(huì)被調(diào)用。
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    // ------------------------------------------- 對(duì) ThreadLocal 進(jìn)行設(shè)置 -------------------------------------------

    // 將 localeContext 和 requestAttributes 存放到 ThreadLocal 中 
    // (其中又具體區(qū)分 ThreadLocal 是否是可繼承的寥闪,上面的帶有previous的LocaleContext與RequestAttributes的獲取都有涉及带膀,這里不做說明)
    initContextHolders(request, localeContext, requestAttributes);

    // --------------------------------------------- 處理請(qǐng)求 ---------------------------------------------------------
    try {
        doService(request, response);
    }
    catch (ServletException | IOException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }

    finally {
        // --------------------------------------------- 請(qǐng)求完成之后的收尾工作 -------------------------------------------

        // 重設(shè)線程本地的數(shù)據(jù)(相當(dāng)于清除本次請(qǐng)求的信息)
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            /* 注意,這是 ServletRequestAttributes 內(nèi)的收尾工作
            * 1. 調(diào)用注冊(cè)的跟request有關(guān)的(也就是scope為0)銷毀方法 (雖然注冊(cè)時(shí)使用的是Runnable類型橙垢,但執(zhí)行時(shí)是直接調(diào)用run方法的垛叨,不是多線程執(zhí)行的)
            * 2. 對(duì)Session屬性進(jìn)行一些更新
            * 3. 標(biāo)記requestActive為false
            */
            requestAttributes.requestCompleted();
        }
        logResult(request, response, failureCause, asyncManager);
        // 向 webApplicationContext 容器內(nèi)發(fā)送一個(gè) ServletRequestHandledEvent 類型的事件
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

// DispatcherServlet
protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
    // 這里獲取的就是Spring MVC啟動(dòng)過程中,initLocaleResolver 策略中加載的默認(rèn)本地化解析器(AcceptHeaderLocaleResolver)
    LocaleResolver lr = this.localeResolver;
    if (lr instanceof LocaleContextResolver) {
        // 如果是CookieLocaleResolver柜某、SessionLocaleResolver嗽元、FixedLocaleResolver才會(huì)到這里
        return ((LocaleContextResolver) lr).resolveLocaleContext(request);
    }
    else {
        // 默認(rèn)的 AcceptHeaderLocaleResolver 會(huì)進(jìn)入這里。
        // 這里的返回值是一個(gè)lambda函數(shù)喂击,其實(shí)是一個(gè)匿名內(nèi)部類的寫法剂癌。
        return () -> (lr != null ? lr.resolveLocale(request) : request.getLocale());
    }
}

// FrameworkServlet
protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request,
        @Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {

    if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
        return new ServletRequestAttributes(request, response);
    }
    else {
        return null;  // preserve the pre-bound RequestAttributes instance
    }
}

// FrameworkServlet
private void initContextHolders(HttpServletRequest request,
        @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {

    if (localeContext != null) {
        LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
    }
    if (requestAttributes != null) {
        RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
    }
}

// WebAsyncUtils
public static WebAsyncManager getAsyncManager(ServletRequest servletRequest) {
    WebAsyncManager asyncManager = null;
    Object asyncManagerAttr = servletRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE);
    if (asyncManagerAttr instanceof WebAsyncManager) {
        asyncManager = (WebAsyncManager) asyncManagerAttr;
    }
    if (asyncManager == null) {
        asyncManager = new WebAsyncManager();
        servletRequest.setAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, asyncManager);
    }
    return asyncManager;
}

1.2.1 ServletRequestAttributes 的使用示例

public void test() {
    ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

    // 獲取 request 和 response
    HttpServletRequest request = requestAttributes.getRequest();
    HttpServletResponse response = requestAttributes.getResponse();
    HttpServletRequest request1 = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

    // 如果scope為0,那么操作的就是此次請(qǐng)求的屬性翰绊,也就是request的請(qǐng)求佩谷;如果scope為1,那么操作的就是session的屬性监嗜。
    Object requestAttr = requestAttributes.getAttribute("屬性名", 0);
    Object sessionAttr = requestAttributes.getAttribute("屬性名", 1);

    requestAttributes.removeAttribute("屬性名", 0);
    requestAttributes.removeAttribute("屬性名", 1);

    // 注冊(cè)一些自定義的邏輯谐檀,在請(qǐng)求處理完后會(huì)在掃尾工作時(shí)調(diào)用這些邏輯,雖然這里是Runnable類型裁奇,但并不是多線程執(zhí)行的桐猬,而是直接調(diào)用的run方法。
    requestAttributes.registerDestructionCallback("1", () -> {}, 0);

    // 獲取session的一些內(nèi)容
    String sessionId = requestAttributes.getSessionId();
    Object sessionMutex = requestAttributes.getSessionMutex();
    HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
}

1.3 DispatcherServlet#doService(HttpServletRequest, HttpServletResponse)

doService方法中中大概做了這么幾件事情
1.對(duì)由include發(fā)起的請(qǐng)求做特殊處理刽肠。

2.將框架內(nèi)的屬性交給HttpServletRequest溃肪,以方便之后的處理流程使用。

~ Spring MVC 的 IOC容器
~ Local 的解析器
~ Theme 的解析器
~ FlashMap 相關(guān)內(nèi)容
3.調(diào)用請(qǐng)求處理的方法(doDispatch(request, response);)音五。

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

    // --------------------------------------- 對(duì)RequestDispatcher#include造成的請(qǐng)求的處理 --------------------------------------------

    // Keep a snapshot of the request attributes in case of an include,
    // to be able to restore the original attributes after the include.
    Map<String, Object> attributesSnapshot = null;
    // 
    /*
    * 檢查請(qǐng)求中是否有 javax.servlet.include.request_uri 屬性惫撰,如果有,就保留請(qǐng)求屬性的快照躺涝,以便能夠在include之后恢復(fù)原始屬性厨钻。
    *
    * 這個(gè)東西和 RequestDispatcher#include(ServletRequest, ServletResponse) 有關(guān),具體內(nèi)容不做說明。
    * 發(fā)揮作用的地方例如: <jsp:incluede page="xxx.jsp"/> 
    */
    if (WebUtils.isIncludeRequest(request)) {
        attributesSnapshot = new HashMap<>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }


    // ------------------------------------------ 將框架內(nèi)的一些對(duì)象塞到request中 --------------------------------------------

    // 將 Spring MVC框架內(nèi)的一些對(duì)象 放到request內(nèi)部莉撇,以使之后的 各種處理器 和 View 使用呢蛤。
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); // Spring MVC的IOC容器
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

    // ----------------------------------------- FlashMapManager的處理 -------------------------------------------------------

    // 這里就是 FlashMapManager 策略起作用的地方惶傻,它的使用在之前 Spring MVC 框架的啟動(dòng)流程中講過
    if (this.flashMapManager != null) {
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            // 將FlashMap放入InputFlashMap中
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        // 因?yàn)檫@是請(qǐng)求的入口棍郎,所以O(shè)utputFlashMap內(nèi)不應(yīng)該存在任何值
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    }

    // ------------------------------------------ 處理請(qǐng)求 -------------------------------------------------------

    try {
        doDispatch(request, response);
    }
    finally {
        // 判斷當(dāng)前請(qǐng)求已經(jīng)調(diào)用過 AsyncWebRequest#startAsync(),不理解可以看完下面對(duì)異步請(qǐng)求的說明后再回頭看這里银室。
        // 如果當(dāng)前請(qǐng)求沒有變更為異步模式的話涂佃,這個(gè)條件才會(huì)成立,如果請(qǐng)求處理方法返回的是異步返回值蜈敢,那條件就不成立辜荠。
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 
            // 在 include 調(diào)度完成后,恢復(fù)上面快照內(nèi)存儲(chǔ)的請(qǐng)求原始屬性
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }
}

1.4 DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)

// DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    // HandlerExecutionChain 實(shí)際上是 HandlerMethod 和 相應(yīng)的攔截器 的組合體
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

        try {
            // -------------------------------------------- 對(duì) multipart/ 類型請(qǐng)求的識(shí)別(例如文件上傳) ------------------------------------------

            // 檢查請(qǐng)求是否是跟multipart/類型的請(qǐng)求相關(guān)抓狭,如果符合條件就把request轉(zhuǎn)為MultipartHttpServletRequest實(shí)現(xiàn)伯病。
            // 條件是: 1.有相應(yīng)Multipart Request解析器 并且 2.是Multipart Request
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // -------------------------------------------- 尋找匹配的請(qǐng)求處理方法 ---------------------------------------------

            // 從 解析路徑映射的處理策略 中 查找到要執(zhí)行的請(qǐng)求處理方法。 由于寫的示例項(xiàng)目是基于注解掃描的否过,所以這里會(huì)使用 RequestMappingHandlerMapping 實(shí)現(xiàn)來查找午笛。
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);  // 默認(rèn)情況下,沒找到匹配的路徑映射就響應(yīng)客戶端404
                return;
            }

            // ---------------------------------------- 尋找合適的 HandlerMethod 執(zhí)行適配器 -------------------------------------

            // 判斷Hanlder/Controller是如何實(shí)現(xiàn)的苗桂,來使用特定的適配器執(zhí)行我們的`請(qǐng)求處理方法`药磺,在示例項(xiàng)目種默認(rèn)情況下會(huì)獲取到 RequestMappingHandlerAdapter 。
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // ------------------------------------------ GET/HDEA請(qǐng)求的緩存機(jī)制 ------------------------------------------
            // https://blog.csdn.net/snail_cjc/article/details/50778855

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
 
            // ------------------------------------------- 執(zhí)行攔截器的PreHandle方法 --------------------------------------------

            // 運(yùn)行攔截器的preHandle方法煤伟,如果有攔截器的preHandle返回了false癌佩,那么此次請(qǐng)求直接停止,同時(shí)調(diào)用攔截器的afterCompletion方法便锨。
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // ---------------------------------------------- 交由HandlerAdapter處理請(qǐng)求 --------------------------------------------------------

            // 調(diào)用`請(qǐng)求處理方法`執(zhí)行邏輯的地方
            mv = ha.handler(processedRequest, response, mappedHandler.getHandler());

            // 判斷當(dāng)前請(qǐng)求已經(jīng)調(diào)用過 AsyncWebRequest#startAsync()围辙,不理解可以看完下面對(duì)異步請(qǐng)求的說明后再回頭看這里。
            // 如果調(diào)用過放案,代表當(dāng)前請(qǐng)求變更為異步模式酌畜,也就是說如果請(qǐng)求處理方法返回的是異步返回值,那條件就成立卿叽。
            if (asyncManager.isConcurrentHandlingStarted()) {
                return; // 如果請(qǐng)求已經(jīng)轉(zhuǎn)為異步模式桥胞,這里直接返回null,不需要執(zhí)行下面的代碼考婴。
            }

            // ------------------------------------------- RequestToViewNameTranslator 邏輯的應(yīng)用 ------------------------------------

            // 如果`請(qǐng)求處理方法`執(zhí)行完成后贩虾,仍然沒有ViewName被設(shè)置(例如:請(qǐng)求處理方法 在 @Controller注解的類中 并且 方法返回值類型為void),
            // 那么就使用 Spring MVC 框架啟動(dòng)過程中初始化的 RequestToViewNameTranslator 策略將請(qǐng)求地址解析為ViewName,
            // 如果解析到的ViewName不為空沥阱,就填充到傳入的 mv 中缎罢。
            applyDefaultViewName(processedRequest, mv);

            // ------------------------------------------- 執(zhí)行攔截器的PostHandle方法 --------------------------------------------

            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            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)前請(qǐng)求是否已經(jīng)轉(zhuǎn)為異步模式,如果是,則條件成立策精,不理解可以看完下面對(duì)異步請(qǐng)求的說明后再回頭看這里舰始。
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                // 調(diào)用 AsyncHandlerInterceptor 類型攔截器的 applyAfterConcurrentHandlingStarted 方法
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // 清理 MultipartRequest 所使用的資源(文件上傳方向的內(nèi)容)。
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

doDispatch都做了什么咽袜?
1.檢查請(qǐng)求是否跟文件上傳相關(guān)丸卷,以及確認(rèn)相關(guān)后的一些轉(zhuǎn)換。(與MultipartResolver策略相關(guān))
2.通過請(qǐng)求找到要執(zhí)行的請(qǐng)求處理方法询刹。(與HandlerMapping策略相關(guān))
3.通過判斷請(qǐng)求處理方法所處的類是使用哪種方式成為Handler來找到合適的HandlerAdapter谜嫉。(與HandlerAdapter策略相關(guān))
4.調(diào)用攔截器的preHandle方法,如果被攔下來了凹联,就執(zhí)行攔截器的afterCompletion方法沐兰。
5.通過獲取到的HandlerAdapter來正式執(zhí)行請(qǐng)求處理方法
6.判斷是否需要將請(qǐng)求路徑解析成ViewName蔽挠。(與RequestToViewNameTranslator策略相關(guān))
7.調(diào)用攔截器的postHandle方法住闯。
8.判斷是否需要渲染視圖煌珊。(與ViewResolvers策略相關(guān))
9.最后調(diào)用攔截器的afterCompletion方法兼蜈。(這一步和第8步是在一個(gè)方法內(nèi)編寫的,但兩件事沒關(guān)系)
10.清理multipart request所產(chǎn)生的資源占用踱葛。

假設(shè)我們使用`@RestController`注解來使類成為`Handler`偶惠,
那么實(shí)際上在`第5步`春寿,`請(qǐng)求處理方法`的返回值就已經(jīng)寫入了Response發(fā)送給了客戶端,
在這種情況下忽孽,第6绑改、8步驟實(shí)際上就不會(huì)發(fā)生,因?yàn)檫@兩部與模板引擎技術(shù)相關(guān)兄一,而在假設(shè)中厘线,我們沒有使用這一技術(shù);

如果我們`@Controller`注解來使類稱為`Handler`出革,同時(shí)沒有使用`@ResponseBody`造壮,那么第6、8步驟將會(huì)作用于`請(qǐng)求處理方法`的返回值骂束。
同時(shí)耳璧,在這種假設(shè)下,我們的返回值可以為:沒有返回值展箱、null旨枯、ViewName、View混驰、ModelAndeView

補(bǔ)充:
1.ViewName 實(shí)際上指的就是一個(gè)字符串而已攀隔,不過這個(gè)字符串會(huì)被認(rèn)為指向了一個(gè)頁面模板或者一個(gè)重定向地址等皂贩,最終會(huì)由不同的 ViewResolvers 解析為具體的 View 實(shí)現(xiàn)。
2.View 是 ViewName 的終點(diǎn)昆汹,每種模板引擎技術(shù)明刷,都會(huì)為我們提供一個(gè)可用的 View 實(shí)現(xiàn),這里可以簡單理解為它就是個(gè)頁面满粗。
3.如果 ModelAndeView 調(diào)用無參構(gòu)造函數(shù)創(chuàng)建辈末,那實(shí)際上和返回一個(gè)null區(qū)別不大。

上面的一串步驟看起來還是很復(fù)雜败潦,這里再簡化一下本冲,方便記憶准脂。
1.對(duì)Http請(qǐng)求進(jìn)行處理劫扒。
2.找到請(qǐng)求處理方法
3.找到合適的HandlerAdapter狸膏。
4.執(zhí)行攔截器的先處理方法沟饥。
5.交由HandlerAdapter執(zhí)行請(qǐng)求處理方法
6.調(diào)用攔截器的后處理方法湾戳。
7.渲染頁面模板
8.清理與收尾贤旷。

在下文會(huì)具體深入說明這些步驟的實(shí)現(xiàn)邏輯

1.4.1 尋找匹配的請(qǐng)求處理方法

// DispatcherServlet
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 默認(rèn)情況下 handlerMappings 里存儲(chǔ)的是 Spring MVC 框架啟動(dòng)時(shí)初始化的兩個(gè)用于解析路徑映射的處理策略
    // BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

// AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    /* 這里要結(jié)合類結(jié)構(gòu)圖來看
    * 如果當(dāng)前 HandlerMapping 是 BeanNameUrlHandlerMapping,則會(huì)前往 AbstractUrlHandlerMapping#getHandlerInternal
    * 如果當(dāng)前 HandlerMapping 是 RequestMappingHandlerMapping砾脑,則會(huì)前往 AbstractHandlerMethodMapping#getHandlerInternal
    * 
    * 最終返回的是要執(zhí)行的請(qǐng)求處理方法幼驶。
    */
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) {
        // 如果處理后handler為字符串, 根據(jù)該字符串拿bean
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

    // 為給定的 請(qǐng)求處理方法 構(gòu)建HandlerExecutionChain,它內(nèi)部包括適用的攔截器韧衣。
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

    if (logger.isTraceEnabled()) {
        logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
        logger.debug("Mapped to " + executionChain.getHandler());
    }

    if (CorsUtils.isCorsRequest(request)) {
        // 配置Cors (跨域資源共享)
        CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    return executionChain;
}

// AbstractHandlerMethodMapping
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 獲取到要尋找的路徑映射
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    this.mappingRegistry.acquireReadLock();
    try {
        // 從 AbstractHandlerMethodMapping.MappingRegistry的實(shí)例對(duì)象內(nèi) 找到對(duì)應(yīng)的 請(qǐng)求處理方法盅藻。
        // AbstractHandlerMethodMapping.MappingRegistry 對(duì)象的構(gòu)建在之前的文章內(nèi)有提到,這里就不重復(fù)了
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

// AbstractHandlerMapping
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    // 轉(zhuǎn)換 handler 的類型
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    // 獲取到要尋找的路徑映射
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);

    // 找到和請(qǐng)求路徑匹配的攔截器畅铭,關(guān)于攔截器的添加方式在下文進(jìn)行說明
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

HandlerExecutionChain實(shí)際上就是HandlerMethodSpring MVC攔截器的組合體氏淑。

1.4.1.1 Spring MVC 攔截器的配置

Spring MVC 默認(rèn)情況下有2種添加方式。

// 第一種添加方式
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>


// 第二種添加方式(手動(dòng)配置HandlerMapping)
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="interceptors">
        <list>
            <bean class="MyInterceptor"/>
        </list>
    </property>
</bean>
// 配置起作用的原理如下

// AbstractHandlerMapping
protected void initApplicationContext() throws BeansException {
    // 一個(gè)空的實(shí)現(xiàn)
    extendInterceptors(this.interceptors);
    // 對(duì)應(yīng)第一種配置方式
    detectMappedInterceptors(this.adaptedInterceptors);
    // 對(duì)應(yīng)第二種配置方式
    initInterceptors();
}

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    mappedInterceptors.addAll(
            BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

protected void initInterceptors() {
    if (!this.interceptors.isEmpty()) {
        for (int i = 0; i < this.interceptors.size(); i++) {
            Object interceptor = this.interceptors.get(i);
            if (interceptor == null) {
                throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
            }
            this.adaptedInterceptors.add(adaptInterceptor(interceptor));
        }
    }
}

1.4.2 尋找合適的HandlerMethod執(zhí)行適配器

因?yàn)槭褂玫氖?code>@Controller/@RestController創(chuàng)建的Handler硕噩,所以最后得到的是RequestMappingHandlerAdapter

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    // 默認(rèn)情況下 handlerAdapters 里存儲(chǔ)的是 Spring MVC 框架啟動(dòng)時(shí)初始化的三個(gè)功能擴(kuò)展適配器假残,每個(gè)都對(duì)應(yīng)了一類Handler的注冊(cè)方式。
    // 在上一篇文章中展示了有4種創(chuàng)建Handler的方式炉擅,這里不再重復(fù)辉懒。
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            // 因?yàn)槲覀兪褂玫氖?@Controller/@RestController 創(chuàng)建的Handler,所以返回的是 RequestMappingHandlerAdapter
            if (adapter.supports(handler)) { 
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

1.4.3 交由HandlerAdapter處理請(qǐng)求

在之前我們就確認(rèn)了谍失,在示例代碼的默認(rèn)情況下最終會(huì)由RequestMappingHandlerAdapter來執(zhí)行眶俩,所以直接看它的代碼。

// AbstractHandlerMethodAdapter
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    return handleInternal(request, response, (HandlerMethod) handler);
}

// RequestMappingHandlerAdapter
protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    // 對(duì) 請(qǐng)求方式 和 Session 的一些檢查
    checkRequest(request);

    // ------------------------ 會(huì)話級(jí)上進(jìn)行代碼同步執(zhí)行 ---------------------------------

    // 在會(huì)話級(jí)別上進(jìn)行代碼同步執(zhí)行袱贮,使得來自同一會(huì)話的請(qǐng)求串行訪問該控制器仿便,沒遇到過合適的使用場景体啰。
    // 默認(rèn)情況下,這個(gè)值是false.
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No HttpSession available -> no mutex necessary
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    else {
        // ------------------------- 默認(rèn)情況下嗽仪,執(zhí)行請(qǐng)求處理方法 ------------------------

        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }

    return mav;
}

// RequestMappingHandlerAdapter
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    /* 
    * 創(chuàng)建一個(gè)持有request和response的ServletWebRequest對(duì)象荒勇。
    * 
    * 這個(gè)類的特殊之處在于它實(shí)現(xiàn)了NativeWebRequest接口,能夠調(diào)用很多HttpServletRequest沒有實(shí)現(xiàn)的方法闻坚,
    * 在操作Last-Modified時(shí)或許會(huì)用到這個(gè)對(duì)象沽翔。
    */ 
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        // ------------------------------ @InitBinder注解 的方法的相關(guān)的處理 -----------------------------

        // 獲取到所有有關(guān)的 @InitBinder 注解的方法,并把他們包裝為一個(gè) WebDataBinderFactory 工廠對(duì)象(新建)
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

        // --------------------------- @SessionAttributes和@ModelAttribute注解 的方法的相關(guān)的處理 -----------------------------

        /*
        * 獲取到 sessionAttributeStore 參數(shù)窿凤,并包裝為一個(gè) SessionAttributesHandler 對(duì)象(新建) 作為 param3仅偎,
        * sessionAttributeStore 的值可以在 Spring MVC 的xml文件中進(jìn)行配置,默認(rèn)實(shí)現(xiàn)是DefaultSessionAttributeStore雳殊。
        *
        * 獲取到所有有關(guān)的 @ModelAttribute 注解的方法 作為 param1橘沥。
        * 
        * 將構(gòu)建的param1、param3和傳入的binderFactor(param2)夯秃,這3個(gè)參數(shù)包裝為一個(gè) ModelFactory 工廠對(duì)象(新建) 
        */
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // ------------------------------- 對(duì)ServletInvocableHandlerMethod的進(jìn)一步組裝 ----------------------------------

        // 使用 ServletInvocableHandlerMethod 對(duì)象實(shí)例包裝 handlerMethod
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        // 設(shè)置參數(shù)解析器座咆,argumentResolvers 的賦值過程在 Spring MVC 框架啟動(dòng)時(shí)進(jìn)行
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        // 設(shè)置返回值解析器,returnValueHandlers 的賦值過程在 Spring MVC 框架啟動(dòng)時(shí)進(jìn)行
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        // 設(shè)置跟 @InitBinder 注解有關(guān)的工廠對(duì)象
        invocableMethod.setDataBinderFactory(binderFactory);
        // 作用就跟名字一樣---參數(shù)名詞發(fā)現(xiàn)者仓洼,用于確認(rèn)方法介陶、構(gòu)造函數(shù)的參數(shù)名稱等作用,這個(gè)參數(shù)可以在Spring MVC的xml文件中配置色建,默認(rèn)實(shí)現(xiàn)是 DefaultParameterNameDiscoverer
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        // ------------------------------ 對(duì)ModelAndViewContainer對(duì)象的進(jìn)一步組裝 ---------------------------------------------

        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        // 取出前文存放到request中的flashMap哺呜,然后存放到mavContainer里持有的的Model中
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        // 把一些相關(guān)的數(shù)據(jù)全部放到 Model 里面 (和 @SessionAttributes 和 @ModelAttribute 兩個(gè)注解相關(guān)的內(nèi)容的處理)
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        // 設(shè)置是否在重定向時(shí)忽略 Model, 不然Model內(nèi)的數(shù)據(jù)會(huì)被拼接到url上, true 就是忽略。
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        // ------------------------------ 對(duì)異步請(qǐng)求相關(guān)的處理 -----------------------------

        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        // 如果當(dāng)前請(qǐng)求是因?yàn)?AsyncContext#dispatch() 方法的調(diào)用而產(chǎn)生箕戳,那么這個(gè)請(qǐng)求內(nèi)部已經(jīng)被Servlet容器設(shè)置了這個(gè)值某残,這里獲取的也就是之前請(qǐng)求創(chuàng)建的那個(gè)實(shí)例。
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        // Callable的攔截器
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        // DeferredResult的攔截器
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        // 如果上一個(gè)請(qǐng)求內(nèi)已經(jīng)設(shè)置了并發(fā)處理結(jié)果漂羊,那么就不需要再執(zhí)行原來的請(qǐng)求處理方法了驾锰,直接處理這里得到的并發(fā)處理結(jié)果就行。
        if (asyncManager.hasConcurrentResult()) { // 能進(jìn)入這個(gè)判斷的請(qǐng)求走越,必定是由 AsyncContext#dispatch() 方法的調(diào)用而產(chǎn)生請(qǐng)求椭豫。
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
            });
            // 這里把并發(fā)處理結(jié)果再包裝成一下,為了配合下面那行代碼的邏輯旨指。
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // ------------------------------ 執(zhí)行請(qǐng)求處理方法并處理返回值 ------------------------------------

        invocableMethod.invokeAndHandle(webRequest, mavContainer);

        // 判斷當(dāng)前請(qǐng)求是否已經(jīng)轉(zhuǎn)為異步模式赏酥,如果是,則條件成立谆构,不理解可以看完下面對(duì)異步請(qǐng)求的說明后再回頭看這里裸扶。
        if (asyncManager.isConcurrentHandlingStarted()) { // 這個(gè)判斷是作用域非 dispatch 導(dǎo)致的請(qǐng)求
            return null;
        }

        // ------------------------------ 收尾工作 ----------------------------------

        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        /* 做了如下三件事情:
        * 調(diào)用我們自己注冊(cè)的所有的 DestructionCallback 方法。 (在上面ServletRequestAttributes的使用示例中有注冊(cè)示例)
        * 將我們通過特定方式訪問的Session屬性進(jìn)行回寫搬素。
        * 把 ServletWebRequest 中的 requestActive 標(biāo)記為 false呵晨。
        */
        webRequest.requestCompleted();
    }
}

1.4.3.1 @InitBinder注解的方法的處理

public static final MethodFilter INIT_BINDER_METHODS = method ->
        AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);

// RequestMappingHandlerAdapter
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
    // 通過 HandlerMethod 獲取到 Hanlder 類型
    Class<?> handlerType = handlerMethod.getBeanType();

    /*
    * 從下面兩段代碼可以看出來 @InitBinder 有多種用法魏保,起碼這里就有2種了
    * 1.在 Hanlder 類中直接定義
    * 2.跟 @ControllerAdvice 注解配合使用
    */

    // -------------------------------- 在Handler中跟 @InitBinder 有關(guān)的代碼 -----------------------------------

    // 從當(dāng)前HandlerMethod所處的類中找到定義的所有@InitBinder注解的方法

    // 首先從緩存中獲取
    Set<Method> methods = this.initBinderCache.get(handlerType);
    if (methods == null) {
        methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
        // 把通過反射找到的類中所有 @InitBinder 方法都放到緩存中,來避免每次都通過反射獲取
        this.initBinderCache.put(handlerType, methods);
    }

    // ------------------------ 跟 @ControllerAdvice 和 @InitBinder 有關(guān)的代碼 ------------------------

    // 從所有被 @ControllerAdvice注解的類中摸屠,找到作用范圍包含當(dāng)前 HandlerMethod所處的類 的第一個(gè)谓罗,
    // 然后將其中定義的所有@InitBinder注解的方法取出來

    List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
    // Global methods first

    // initBinderAdviceCache 的賦值發(fā)生在Spring MVC 啟動(dòng)過程中(對(duì) HandlerAdapter 策略初始化的時(shí)候)
    // key與@ControllerAdvice注解的類有關(guān)   value是其中定義的被@InitBinder注解的所有方法
    this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
        // 和@ControllerAdvice注解的作用范圍有關(guān),這個(gè)注解也是可以配置屬性的季二,例如:basePackages檩咱、assignableTypes等
        if (clazz.isApplicableToBeanType(handlerType)) { 
            Object bean = clazz.resolveBean();
            for (Method method : methodSet) {
                initBinderMethods.add(createInitBinderMethod(bean, method)); // 太過細(xì)節(jié),不做過多關(guān)注
            }
        }
    });

    // -------------------- 將兩種方式找到的 @InitBinder 合并到一個(gè)集合中 --------------------------------

    // 將 methods 集合轉(zhuǎn)換類型并存放到 initBinderMethods 中胯舷,initBinderMethods 是個(gè)List集合
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        initBinderMethods.add(createInitBinderMethod(bean, method));
    }
    // 使用 ServletRequestDataBinderFactory 對(duì)象包裝 這些 @InitBinder方法刻蚯,以方便后續(xù)使用
    return createDataBinderFactory(initBinderMethods);
}

// RequestMappingHandlerAdapter
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
        throws Exception {

    return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}

// RequestMappingHandlerAdapter
public WebBindingInitializer getWebBindingInitializer() {
    return this.webBindingInitializer;
}

從上面的代碼中可以直觀的看出來@InitBinder的2種使用方式,實(shí)際上有3種桑嘶,
第三種就是getWebBindingInitializer()獲取的webBindingInitializer炊汹,這個(gè)東西我們可以通過配置來設(shè)置。
下面進(jìn)行展示3種使用方法不翩。

// com.demo.i.controller.MyController


// 第一種用法(在Handler中直接使用兵扬,這種方式只針對(duì)當(dāng)前Handler生效)
@RestController
public class MyController {
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        // do something...
    }
}

// 第二種用法(配合@ControllerAdvice進(jìn)行全局配置麻裳,這種方式只針對(duì)掃描到的Handler生效)
// 如果@ControllerAdvice不指定范圍口蝠,則代表針對(duì)所有Handler生效

@ControllerAdvice // (basePackages = "com.demo.i.controller")
public class MyControllerAdvice {
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        // do something...
    }
}

// 第三種用法(全局配置)
@Bean
public RequestMappingHandlerAdapter webBindingInitializer() {
    RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
    adapter.setWebBindingInitializer(new WebBindingInitializer() {

        // WebBindingInitializer中有兩個(gè)入?yún)⒌姆椒ㄒ呀?jīng)被啟用
        @Override
        public void initBinder(WebDataBinder binder) {
            // do something...
        }
    });
    return adapter;
}

1.4.3.2 @SessionAttributes和@ModelAttribute注解的方法的處理

private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();

// 注意,這里是沒有@RequestMapping注解津坑,但有@ModelAttribute注解妙蔗,不要少看了那個(gè)!
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
        (!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
                AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));

// RequestMappingHandlerAdapter
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
    // -------------------------------- 找到關(guān)聯(lián)的SessionAttributesHandler ----------------------------
    // -------------------------------- 這個(gè)東西是處理 @SessionAttributes 注解用的 -----------------------

    // 找到和當(dāng)前 handlerMethod 有關(guān)系的 SessionAttributesHandler
    SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);

    // 通過 HandlerMethod 獲取到 Hanlder 類型
    Class<?> handlerType = handlerMethod.getBeanType();

    // -------------------------------- 找到Handler中被 @ModelAttribute 注釋的方法 -------------------------------

    Set<Method> methods = this.modelAttributeCache.get(handlerType);
    if (methods == null) {
        methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
        this.modelAttributeCache.put(handlerType, methods);
    }

    // ------------------------ 跟 @ControllerAdvice 和 @ModelAttribute 有關(guān)的代碼 ------------------------
    // 從所有被 @ControllerAdvice注解的類中疆瑰,找到作用范圍包含當(dāng)前 HandlerMethod所處的類 的第一個(gè)眉反,
    // 然后將其中定義的所有被@ModelAttribute注解的方法取出來

    List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
    // Global methods first

    // modelAttributeAdviceCache 的賦值發(fā)生在Spring MVC 啟動(dòng)過程中(對(duì) HandlerAdapter 策略初始化的時(shí)候)
    // key與@ControllerAdvice注解的類有關(guān)   value是其中定義的被@InitBinder注解的所有方法
    this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
        if (clazz.isApplicableToBeanType(handlerType)) {
            Object bean = clazz.resolveBean();
            for (Method method : methodSet) {
                attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
            }
        }
    });

    // -------------------- 將兩種方式找到的方法合并到一個(gè)集合中 --------------------------------

    // 將 methods 集合轉(zhuǎn)換類型并存放到 attrMethods 中,attrMethods 是個(gè)List集合
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
    }
    return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

// RequestMappingHandlerAdapter
private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
    // 通過 HandlerMethod 獲取到 Hanlder 類型
    Class<?> handlerType = handlerMethod.getBeanType();

    // sessionAttributesHandlerCache 使用的Map是 ConcurrentHashMap 實(shí)現(xiàn)
    SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
    // DCL模式(Double Check Lock)
    if (sessionAttrHandler == null) {
        synchronized (this.sessionAttributesHandlerCache) {
            sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
            if (sessionAttrHandler == null) {
                // 創(chuàng)建一個(gè)跟這個(gè)Handler有關(guān)系的SessionAttributesHandler對(duì)象實(shí)例穆役。
                // sessionAttributeStore 默認(rèn)使用的是 DefaultSessionAttributeStore 實(shí)現(xiàn)寸五。
                sessionAttrHandler = new SessionAttributesHandler(handlerType, this.sessionAttributeStore);
                this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler);
            }
        }
    }
    return sessionAttrHandler;
}

public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
    Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
    this.sessionAttributeStore = sessionAttributeStore;

    // 這里是對(duì) @SessionAttributes 注解的處理,嘗試從Handler上面找到 @SessionAttributes注解
    SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
    if (ann != null) {
        Collections.addAll(this.attributeNames, ann.names());
        Collections.addAll(this.attributeTypes, ann.types());
    }
    this.knownAttributeNames.addAll(this.attributeNames);
}
1.4.3.2.1 @ModelAttribute

從上面的代碼中可以直觀的看出來@ModelAttribute的2種總體使用方式耿币,下面進(jìn)行展示2種總體使用方法梳杏。

關(guān)于ModelModelMap淹接、ModelAndView十性,這是內(nèi)容都是要配合ViewResolver使用的。
也就是說塑悼,這些內(nèi)容都是和模板引擎有關(guān)的劲适,可以理解為就是負(fù)責(zé)在控制器和展現(xiàn)數(shù)據(jù)的視圖之間傳遞數(shù)據(jù)用的。
其實(shí)這里的Model體現(xiàn)的就是MVC模式中的M厢蒜,在下文中出現(xiàn)的Model霞势、ModelMap實(shí)際指的是一個(gè)東西烹植。

// 第一種用法(在Handler中直接使用,這種方式只針對(duì)當(dāng)前Handler生效)
@Controller
public class MyController {
    // 提供了更加詳細(xì)的用法

    // 如果訪問 /test愕贡,這個(gè)方法會(huì)比test更早執(zhí)行
    // 搶先接收到參數(shù)a刊橘,然后進(jìn)行處理,最后以a作為key添加到Model中颂鸿,在test中嘗試從Model中獲取屬性a時(shí)就可以拿到了
    @ModelAttribute
    public void myModelAttribute(@RequestParam String a, Model model) {
        Map<String, Object> map = model.asMap();
        map.put("a", a + " -> myModelAttribute -> test");
    }

    // 如果訪問 /test促绵,這個(gè)方法會(huì)比test更早執(zhí)行
    // 搶先接收到參數(shù)a,然后進(jìn)行處理嘴纺,最后以b作為key返回給Model败晴,在test中嘗試從Model中獲取屬性b時(shí)就可以拿到了
    @ModelAttribute("b") // @ModelAttribute的value值將作為key,方法的返回值將作為value栽渴,這個(gè)value可以不寫尖坤,但Spring內(nèi)部解析時(shí)會(huì)變得復(fù)雜,使程序運(yùn)行變慢
    public String myModelAttribute1(@RequestParam String a) {
        return a + " -> myModelAttribute1 -> test";
    }

    // 測試請(qǐng)求 Get localhost/test?a=1
    @ResponseBody
    @GetMapping("/test")
    public String test(@ModelAttribute("a") String a, @ModelAttribute("b") String b) {
        return a + "\n" + b;
    }

    @GetMapping("/page/test")
    public String test(Model model) { // model 在頁面模板上就可以使用
        return "test"; // ViewName
    }
}

// 第二種使用方式(配合@ControllerAdvice進(jìn)行全局配置闲擦,這種方式只針對(duì)掃描到的Handler生效)
// 如果@ControllerAdvice不指定范圍慢味,則代表針對(duì)所有Handler生效
@ControllerAdvice // (basePackages = "com.demo.i.controller")
public class MyControllerAdvice {
    @ModelAttribute
    public void myModelAttribute() {
        // do something...
    }
}
1.4.3.2.2 @SessionAttribute 和 @SessionAttributes

根據(jù)源碼,可以看出來@SessionAttributes是要加在Handler上來使用的墅冷。
@SessionAttributes用于在Session中存儲(chǔ)Model內(nèi)部的數(shù)據(jù)纯路,@SessionAttribute則用于獲取存儲(chǔ)在Session中的數(shù)據(jù)。

@Controller
@SessionAttributes(names = {"a", "b"})
public class MyController {
    @RequestMapping("/test1")
    public String test1(ModelMap modelMap, HttpSession session) {
        modelMap.addAttribute("a", "1");
        modelMap.addAttribute("b", "2");

        session.setAttribute("c", "10");
        return "redirect:/test2";
    }

    @RequestMapping("/test2")
    public String test2(ModelMap modelMap, HttpSession session) {
        // ModelMap 中可以獲取到 @SessionAttributes 注解中標(biāo)記的屬性寞忿,但獲取不到直接存入Session中的屬性
        System.out.println(modelMap.get("a"));  // 1
        System.out.println(modelMap.get("b"));  // 2
        System.out.println(modelMap.get("c"));  // null

        // 可以直接通過Session獲取到@SessionAttributes存儲(chǔ)的屬性
        System.out.println(session.getAttribute("a"));  // 1
        System.out.println(session.getAttribute("b"));  // 2
        System.out.println(session.getAttribute("c"));  // 10
        return "redirect:/test3";
    }

    @RequestMapping("/test3")
    public String test3(@SessionAttribute("a") String a,
                        @SessionAttribute("c") String c,
                        SessionStatus sessionStatus) {
        // @SessionAttribute 注解可以獲取到Session內(nèi)的數(shù)據(jù)
        System.out.println(a);  // 1
        System.out.println(c);  // 10

        // 清除 @SessionAttributes 注解標(biāo)記的屬性驰唬,對(duì)應(yīng)的數(shù)據(jù)會(huì)從Session中刪除
        sessionStatus.setComplete();
        return "redirect:/test4";
    }

    @RequestMapping("/test4")
    public String test4(HttpSession session) {
        // 因?yàn)樯弦粋€(gè)請(qǐng)求中已經(jīng)清除了 @SessionAttributes 對(duì)屬性的標(biāo)記,所以a和b已經(jīng)被刪除了
        System.out.println(session.getAttribute("a")); // null
        System.out.println(session.getAttribute("b")); // null
        System.out.println(session.getAttribute("c")); // 10

        return "test4"; // ViewName
    }
}

補(bǔ)充腔彰,有一個(gè)@RequestAttribute注解叫编,它干的活和@SessionAttribute一樣,只不過它的作用范圍是RequestScope霹抛。

1.4.3.3 對(duì)ModelAndViewContainer對(duì)象的進(jìn)一步組裝

1.4.3.3.1 將FlashMap中的屬性存入Model中
// 取出前文存放到request中的flashMap搓逾,然后存放到mavContainer里持有的的Model中
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));

在上面的RequestMappingHandlerAdapter#invokeHandlerMethod源碼里有如上這么一行代碼,包括本文的上面也有對(duì)FlashMapManager的處理杯拐,再結(jié)合我們之前在Spring的啟動(dòng)過程一文中說過的FlashMap的用法霞篡,這里進(jìn)行一下補(bǔ)充。

@PostMapping("/a")
public void a(HttpServletRequest request, HttpServletResponse response) throws IOException {
    FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
    flashMap.put("a", "1");
    flashMap.put("b", "2");

    String redirect = "/b";
    // 將 FlashMap 交由 FlashMapManager 管理
    RequestContextUtils.saveOutputFlashMap(redirect, request, response);
    response.sendRedirect(redirect);
}

@GetMapping("/b")
public String b(HttpServletRequest request, ModelMap modelMap) {
    Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
    Object returnValue = null;
    if (inputFlashMap != null) {
        returnValue = inputFlashMap.get("a");
    }
    return (String) returnValue;
}

相對(duì)于之前提供的寫法藕施,這里只是在b方法上添加了一個(gè)ModelMap入?yún)ⅰ?br> 這里要補(bǔ)充的就是寇损,根據(jù)上面的源碼的意思,可以在目標(biāo)方法上使用Model接收到FlashMap內(nèi)的數(shù)據(jù)裳食。

1.4.3.3.2 將和@SessionAttributes和@ModelAttribute相關(guān)的屬性存入Model中
// ModelFactory
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
        throws Exception {
    // 從 Session 中獲取 @SessionAttributes 中指定的屬性集
    Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
    // 合并屬性矛市,將 sessionAttributes 內(nèi)的數(shù)據(jù)也添加到 Model 中
    container.mergeAttributes(sessionAttributes);
    // 運(yùn)行 @ModelAttribute 注解注釋的方法,如果有返回值 且 binding = true诲祸,則將返回值添加到 Model 中浊吏。
    invokeModelAttributeMethods(request, container);

    // 找到所有被 @ModelAttribute 注解 同時(shí) 又被 @SessionAttributes 指定的參數(shù)
    for (String name : findSessionAttributeArguments(handlerMethod)) {
        // 如果 Model 中不包含這個(gè)屬性而昨,就嘗試從 Session 中獲取這個(gè)屬性然后添加到 Model 中,如果 Session 里面也沒有這個(gè)參數(shù)就直接報(bào)錯(cuò)找田。
        if (!container.containsAttribute(name)) {
            Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
            if (value == null) {
                throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
            }
            container.addAttribute(name, value);
        }
    }
}

// ModelFactory
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
        throws Exception {

    // modelMethods 內(nèi)容的填充是在 RequestMappingHandlerAdapter#getModelFactory 方法的最后一行創(chuàng)建 ModelFactory 對(duì)象實(shí)例時(shí)進(jìn)行的歌憨。
    // 它實(shí)際上存放的就是被 @ModelAttribute 注解注釋的方法。
    while (!this.modelMethods.isEmpty()) {
        // 獲取 @ModelAttribute 注解的方法
        InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
        // 獲取到 @ModelAttribute 注解
        ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
        Assert.state(ann != null, "No ModelAttribute annotation");
        // 判斷 @ModelAttribute 中設(shè)置的 name/value 值是否在 Model 中存在
        if (container.containsAttribute(ann.name())) {
            // 這個(gè)是 binding = false 時(shí)的情況墩衙,默認(rèn)情況下 binding = true务嫡,這里不具體說了,意思很明確了
            if (!ann.binding()) {
                container.setBindingDisabled(ann.name());
            }
            continue;
        }

        // 這下面是處理 binding = true 的情況

        // 調(diào)用這個(gè)被 @ModelAttribute 注解注釋的方法
        Object returnValue = modelMethod.invokeForRequest(request, container);
        if (!modelMethod.isVoid()){
            // 獲取到 @ModelAttribute 注解設(shè)置的value值漆改;如果沒有設(shè)置心铃,則會(huì)通過反射將返回值的類型解析為value
            // 所以還是盡量寫上value值
            String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
            if (!ann.binding()) {
                container.setBindingDisabled(returnValueName);
            }
            // 如果 Model 中沒有這個(gè)屬性,則將該屬性添加到 Model 中
            if (!container.containsAttribute(returnValueName)) {
                container.addAttribute(returnValueName, returnValue);
            }
        }
    }
}

上面的代碼中提供了一種@SessionAttributes@ModelAttribute結(jié)合起來的使用方法挫剑,示例代碼如下去扣。

// 都是一些花里胡哨的用法。

@RestController
@SessionAttributes("test")
public class Test {
    @ModelAttribute
    public void a(HttpSession session) {
        session.setAttribute("test", "123");
    }

    @RequestMapping("/test1")
    public String test1(@ModelAttribute("test") String test) {
        return test;
    }
}

1.4.3.4 執(zhí)行請(qǐng)求處理方法并處理返回值

// ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // ----------------------------------------- 執(zhí)行請(qǐng)求處理方法 -----------------------------------------

    // 這里面主要就是使用參數(shù)解析器對(duì)方法入?yún)⒌慕馕龇疲馕鐾陞?shù)后直接使用反射調(diào)用請(qǐng)求處理方法愉棱,這里就不細(xì)說了,感興趣自己看
    // 其中也會(huì)使用到在在之前就已經(jīng)準(zhǔn)備好的 @InitBinder 注解的相關(guān)的方法
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

    // ----------------------------------------- 設(shè)置響應(yīng)狀態(tài) -------------------------------------------

    // 通過 @ResponseStatus 方式主動(dòng)設(shè)置
    setResponseStatus(webRequest);

    if (returnValue == null) { // 判斷請(qǐng)求值是否為null
        /* 這里有3個(gè)條件哲戚,只要有一個(gè)達(dá)成就行
        * 第一個(gè)和 Get 類型請(qǐng)求的緩存有關(guān)奔滑,如果符合緩存情況整個(gè)判斷就為true。
        * 第二個(gè)和 @ResponseStatus 注解有關(guān)惫恼,如果有這個(gè)注解整個(gè)判斷就為true档押。
        * 第三個(gè) 用來在ModelAndView中表示請(qǐng)求已經(jīng)執(zhí)行完成
        */
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) { // 判斷 @ResponseStatus 有沒有設(shè)置 reason 值,如果設(shè)置了祈纯,就不需要處理返回值了。
        mavContainer.setRequestHandled(true); // 標(biāo)記請(qǐng)求已經(jīng)處理完成叼耙。
        return;
    }

    // ----------------------------------------- 處理 請(qǐng)求處理方法的返回值 ------------------------------------
    // 如果請(qǐng)求處理方法有返回值且沒有使用@ResponseStatus注解腕窥,就需要走下面的邏輯

    mavContainer.setRequestHandled(false); // 標(biāo)記請(qǐng)求尚為完成處理
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 從 returnValueHandlers 中找到適合處理當(dāng)前返回值的 返回值處理器,將返回值交由返回值處理器處理
        // 不同的返回值處理器中 會(huì)在各自合適的時(shí)機(jī)將 ModelAndViewContainer 中的 RequestHandled 設(shè)置為true
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}

returnValueHandlers也就是返回值處理器筛婉。
如果使用了方法直接或間接使用了@ResponseBody注解簇爆,則使用RequestResponseBodyMethodProcessor來處理返回值,通過I/O寫入response爽撒。
如果返回值使用ResponseEntity入蛆,則使用HttpEntityMethodProcessor來處理返回值。
里面有支持異步返回值的返回值處理器硕勿,例如AsyncTaskMethodReturnValueHandler哨毁、CallableMethodReturnValueHandlerDeferredResultMethodReturnValueHandler源武。

補(bǔ)充: 如果方法沒有直接或間接使用@ResponseBody注解扼褪,那返回值則是有嚴(yán)格限制的想幻,可以試試返回int類型的值,Spring MVC會(huì)找不到合適的處理器而相應(yīng)請(qǐng)求處理失敗话浇。

1.4.3.5 收尾工作

收尾工作做了如下幾方面的工作脏毯。
1.對(duì)@SessionAttributes注解的處理
2.組裝ModelAndView對(duì)象
3.對(duì)FlashMap的處理

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
        ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
    // -------------------------- 對(duì) @SessionAttributes 注解的處理 -----------------------------

    // 將列為 @SessionAttributes 的 Model 屬性升級(jí)到會(huì)話級(jí)別,必要時(shí)添加 BindingResult 屬性幔崖。
    modelFactory.updateModel(webRequest, mavContainer);
    if (mavContainer.isRequestHandled()) { // 如果請(qǐng)求已經(jīng)處理完成
        return null;
    }
    ModelMap model = mavContainer.getModel();

    // ------------------------- 通過 ModelAndViewContainer 構(gòu)建 ModelAndView --------------------

    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
    if (!mavContainer.isViewReference()) {
        mav.setView((View) mavContainer.getView()); // 設(shè)置視圖
    }

    // ------------------------- 處理 FlashMap 相關(guān)內(nèi)容 -----------------------------

    // 判斷 Model 是否使 RedirectAttributes 類型食店,這個(gè)東西和 FlashMap 的使用有關(guān)。
    if (model instanceof RedirectAttributes) { 
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request != null) {
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
    }
    return mav;
}

在上一篇說明Spring MVC框架啟動(dòng)過程的文章里赏寇,已經(jīng)說了一種我比較喜歡的FlashMap使用方法叛买。
在這里,從上面的源碼中可以看到另一種FlashMap的使用方式蹋订,示例代碼如下展示率挣。

@GetMapping("/test1")
public String test1(RedirectAttributes attributes) {
    attributes.addFlashAttribute("a", "10");
    return "redirect:/test2";
}

@ResponseBody
@GetMapping("/test2")
public Object test2(ModelMap model) {
    return model.getAttribute("a");
}

1.4.4 處理最終的視圖

// DispatcherServlet
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
        @Nullable Exception exception) throws Exception {

    boolean errorView = false;

    // 如果存在異常,就嘗試獲取異常相關(guān)的view
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        // 如果請(qǐng)求處理程序返回了要渲染的視圖露戒,就渲染視圖
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

// DispatcherServlet
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // -------------------------------------- 處理 Locale ------------------------------

    // Determine locale for request and apply it to the response.
    Locale locale =
            (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
    response.setLocale(locale);

    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        // 如果存在ViewName椒功,就通過合適的ViewResolver解析出來一個(gè)View。
        // 通常我們使用模板引擎時(shí)會(huì)配置一個(gè)ViewResolver以及一個(gè)特定的View
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        if (view == null) {
            // 如果沒有合適的ViewResolver智什,則會(huì)出現(xiàn)如下異常动漾,通常出現(xiàn)這個(gè)異常意味著我們模板引擎沒有配置好。
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                    "' in servlet with name '" + getServletName() + "'");
        }
    }
    else {
        // 如果我們?cè)谡?qǐng)求處理方法返回的時(shí)候直接創(chuàng)建好了View對(duì)象荠锭,則會(huì)進(jìn)入這里旱眯。
        view = mv.getView();
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                    "View object in servlet with name '" + getServletName() + "'");
        }
    }

    // Delegate to the View object for rendering.
    if (logger.isTraceEnabled()) {
        logger.trace("Rendering view [" + view + "] ");
    }
    try {
        if (mv.getStatus() != null) {
            response.setStatus(mv.getStatus().value());
        }
        // 使用不同模板引擎時(shí),這個(gè)View對(duì)象的類型是不同的证九,往往是模板引擎的依賴內(nèi)自帶的删豺,通過它們內(nèi)部的細(xì)節(jié)進(jìn)行頁面渲染。
        view.render(mv.getModelInternal(), request, response);
    }
    catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error rendering view [" + view + "]", ex);
        }
        throw ex;
    }
}

1.5 Spring MVC中的異步請(qǐng)求的邏輯

要想理解上面源碼中的異步請(qǐng)求的意義愧怜,首先要知道異步請(qǐng)求的邏輯是如何運(yùn)行的呀页,之后再回頭看上面的代碼,才能更好的理解拥坛。
這部分邏輯主要涉及4個(gè)類蓬蝶,WebAsyncManagerAsyncWebRequest猜惋、AsyncContext丸氛、AsyncListener
AsyncWebRequest使用的是StandardServletAsyncWebRequest作為具體實(shí)現(xiàn)著摔。
AsyncContext這個(gè)接口是Servlet的技術(shù)缓窜。
它們的關(guān)系是這樣的:WebAsyncManager類中持有一個(gè)StandardServletAsyncWebRequest的對(duì)象實(shí)例,而StandardServletAsyncWebRequest內(nèi)部又是對(duì)AsyncContext接口的調(diào)用。

ServletRequest API文檔
AsyncContext API文檔
AsyncListener API文檔

1.5.1 異步返回值處理器

可以通過返回值處理器來作為理解這一塊邏輯的起點(diǎn)雹洗,Spring MVC中異步返回值常用的有Callable香罐、DeferredResultStreamingResponseBody等时肿。
這里以Callable為例進(jìn)行說明庇茫,可以找到它的返回值處理器,也就是CallableMethodReturnValueHandler螃成。

// 使用示例

// web.xml
<servlet>
    <!-- Spring MVC DispatcherServlet 的配置 -->
    <async-supported>true</async-supported>
</servlet>

@ResponseBody
@GetMapping("/test")
public Callable<String> test() {
    return () -> {
        Thread.sleep(1000 * 2);
        return "test"; // 當(dāng)使用 Callable 時(shí)這里也可以返回 ViewName
    };
}
// Callable類型返回值的處理器
public class CallableMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        // 對(duì) Callable 返回值的支持
        return Callable.class.isAssignableFrom(returnType.getParameterType());
    }

    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

        if (returnValue == null) {
            mavContainer.setRequestHandled(true);
            return;
        }

        // returnValue 就是 請(qǐng)求處理方法 的返回值
        Callable<?> callable = (Callable<?>) returnValue;

        // 這里 getAsyncManager旦签,會(huì)從 request 中獲取之前存儲(chǔ)的 WebAsyncManager,隨和的startCallableProcessing才是主要邏輯
        WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(callable, mavContainer);
    }

}

1.5.2 WebAsyncManager#startCallableProcessing

要想全面的理解寸宏,這里要結(jié)合RequestMappingHandlerAdapter#invokeHandlerMethod方法的部分代碼片段來看宁炫。

// RequestMappingHandlerAdapter#invokeHandlerMethod 內(nèi)的代碼片段
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors); // 注冊(cè) Callable 類型返回值的攔截器,可以在Xml文件中配置
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);


// WebAsyncManager
public void startCallableProcessing(Callable<?> callable, Object... processingContext) throws Exception {
    Assert.notNull(callable, "Callable must not be null");
    // 這里會(huì)把 Callable 包裝成 WebAsyncTask氮凝,請(qǐng)求處理方法 的返回值實(shí)際上可以直接是 WebAsyncTask 類型羔巢。
    startCallableProcessing(new WebAsyncTask(callable), processingContext);
}

// WebAsyncManager
public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object... processingContext)
        throws Exception {

    Assert.notNull(webAsyncTask, "WebAsyncTask must not be null");
    Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");

    // 這里由于返回值類型是Callable,所以這里這個(gè)Timeout是空的罩阵,可以直接使用WebAsyncTask作為返回值竿秆,這樣就能具體設(shè)置超時(shí)時(shí)間
    Long timeout = webAsyncTask.getTimeout();
    if (timeout != null) {
        this.asyncWebRequest.setTimeout(timeout);
    }

    // 這里由于返回值類型是Callable,所以這里這個(gè)Executor是空的稿壁,可以直接使用WebAsyncTask作為返回值幽钢,這樣就能具體設(shè)置Executor的實(shí)現(xiàn)
    AsyncTaskExecutor executor = webAsyncTask.getExecutor();
    if (executor != null) {
        this.taskExecutor = executor;
    }
    else {
        logExecutorWarning();
    }

    // ------------------------------------------------- 整合Callable處理攔截器 -------------------------------------------------

    List<CallableProcessingInterceptor> interceptors = new ArrayList<>();
    // 這個(gè)比較特殊,是在 WebAsyncTask#getInterceptor() 方法中定義的一個(gè)匿名內(nèi)部類
    interceptors.add(webAsyncTask.getInterceptor());
    // 在 RequestMappingHandlerAdapter#invokeHandlerMethod 中有批量注冊(cè)傅是,但默認(rèn)沒有值匪燕,用的時(shí)候需要自己配置
    // 在 FrameworkServlet#processRequest 中注冊(cè)了一個(gè) RequestBindingInterceptor
    interceptors.addAll(this.callableInterceptors.values()); 
    // timeoutCallableInterceptor 是 TimeoutCallableProcessingInterceptor 類的實(shí)例對(duì)象
    interceptors.add(timeoutCallableInterceptor);

    final Callable<?> callable = webAsyncTask.getCallable();
    // 將攔截器集合封裝成一個(gè)CallableInterceptorChain實(shí)例對(duì)象。
    final CallableInterceptorChain interceptorChain = new CallableInterceptorChain(interceptors);

    // ------------------------------------------------ 添加一系列的行為處理器 -------------------------------------------
    // ------------------------ 這些行為處理器的邏輯會(huì)在AsyncListener的回調(diào)中被執(zhí)行喧笔,是一個(gè)重要的邏輯區(qū)域帽驯。 ----------------------

    // 3(指在這個(gè)方法中發(fā)揮作用的順序)
    // 執(zhí)行超時(shí)后的處理器  (#1.5.4 節(jié)可以看到調(diào)用)
    this.asyncWebRequest.addTimeoutHandler(() -> {
        logger.debug("Async request timeout for " + formatRequestUri());
        Object result = interceptorChain.triggerAfterTimeout(this.asyncWebRequest, callable);
        if (result != CallableProcessingInterceptor.RESULT_NONE) {
            setConcurrentResultAndDispatch(result);
        }
    });

    // 執(zhí)行異常后的處理器  (#1.5.4 節(jié)可以看到調(diào)用)
    this.asyncWebRequest.addErrorHandler(ex -> {
        logger.debug("Async request error for " + formatRequestUri() + ": " + ex);
        Object result = interceptorChain.triggerAfterError(this.asyncWebRequest, callable, ex);
        result = (result != CallableProcessingInterceptor.RESULT_NONE ? result : ex);
        setConcurrentResultAndDispatch(result);
    });

    // 執(zhí)行完成后的處理器  (#1.5.4 節(jié)可以看到調(diào)用)
    this.asyncWebRequest.addCompletionHandler(() ->
            interceptorChain.triggerAfterCompletion(this.asyncWebRequest, callable)); 

    // ----------------------------------- 調(diào)用攔截器的 beforeConcurrentHandling 方法 -----------------------------

    interceptorChain.applyBeforeConcurrentHandling(this.asyncWebRequest, callable);

    // ------------------------------------ 將Servlet容器委派的請(qǐng)求轉(zhuǎn)換為異步模式 ------------------------------------------

    // 1(指在這個(gè)方法中發(fā)揮作用的順序)
    startAsyncProcessing(processingContext);

    // ----------------------------------- 對(duì) 請(qǐng)求處理方法的返回值 進(jìn)行再包裝 -------------------------------------------

    // 將 Callable 類型的返回值封裝為 Future,然后提交給 taskExecutor 等待執(zhí)行溃斋。
    try {
        // 2(指在這個(gè)方法中發(fā)揮作用的順序)
        Future<?> future = this.taskExecutor.submit(() -> {
            Object result = null;
            try {
                // ---------------------------- 調(diào)用攔截器的 preProcess 方法(正序遍歷攔截器) -----------------------------
                interceptorChain.applyPreProcess(this.asyncWebRequest, callable);

                // ---------------------------- 執(zhí)行返回的 請(qǐng)求處理方法 的返回值 ----------------------------------------
                // 這個(gè)方法如果執(zhí)行時(shí)間太久導(dǎo)致請(qǐng)求響應(yīng)超過了指定時(shí)間界拦,就會(huì)執(zhí)行超時(shí)的邏輯
                result = callable.call();
            }
            catch (Throwable ex) { // 這里是可以響應(yīng)中斷邏輯的,當(dāng)超時(shí)后或Future執(zhí)行異常后梗劫,程序會(huì)對(duì)的線程進(jìn)行中斷
                result = ex;
            }
            finally {
                // ---------------------------- 調(diào)用攔截器的 postProcess 方法(倒序遍歷攔截器) -----------------------------
                result = interceptorChain.applyPostProcess(this.asyncWebRequest, callable, result);
            }
            // 處理異步請(qǐng)求結(jié)果 并且 通過調(diào)用 AsyncContext#dispatch 來對(duì)當(dāng)前請(qǐng)求的URI再次調(diào)度 (意思就是會(huì)對(duì)當(dāng)前URI再發(fā)送一個(gè)請(qǐng)求)
            // 能走到這里就代表 異步操作 是能完成的,代表會(huì)執(zhí)行完成的邏輯截碴。
            setConcurrentResultAndDispatch(result);
        });

        // 設(shè)置的這個(gè) TaskFuture梳侨,在之后的超時(shí)和異常處理中有用到。
        interceptorChain.setTaskFuture(future);
    }
    catch (RejectedExecutionException ex) {
        Object result = interceptorChain.applyPostProcess(this.asyncWebRequest, callable, ex);
        setConcurrentResultAndDispatch(result);
        throw ex;
    }
}

1.5.3 將請(qǐng)求轉(zhuǎn)換為異步模式

// WebAsyncManager
private void startAsyncProcessing(Object[] processingContext) {
    synchronized (WebAsyncManager.this) {
        // concurrentResult 是用來存放最后的處理結(jié)果的日丹,RESULT_NONE 代表沒有結(jié)果
        this.concurrentResult = RESULT_NONE; 
        // 這里這個(gè)東西走哺,上面?zhèn)鬟^來的是ModelAndViewContainer
        this.concurrentResultContext = processingContext;
    }

    // ------------------------------------ 將請(qǐng)求轉(zhuǎn)換為異步模式 ------------------------------------------
    this.asyncWebRequest.startAsync();

    if (logger.isDebugEnabled()) {
        logger.debug("Started async request");
    }
}

// StandardServletAsyncWebRequest
public void startAsync() {
    Assert.state(getRequest().isAsyncSupported(),
            "Async support must be enabled on a servlet and for all filters involved " +
            "in async request processing. This is done in Java code using the Servlet API " +
            "or by adding \"<async-supported>true</async-supported>\" to servlet and " +
            "filter declarations in web.xml.");
    Assert.state(!isAsyncComplete(), "Async processing has already completed");

    // 如果當(dāng)前請(qǐng)求已經(jīng)調(diào)用過 startAsync,則直接返回哲虾,防止形成嵌套式調(diào)用丙躏。
    if (isAsyncStarted()) { 
        return;
    }

    // 這里才是核心點(diǎn)择示,使用`startAsync`、`addListener`晒旅、`setTimeout`這些`Servlet`體系提供的方法栅盲,
    // 來完成Spring MVC框架的異步請(qǐng)求的邏輯

    this.asyncContext = getRequest().startAsync(getRequest(), getResponse()); // 將當(dāng)前請(qǐng)求轉(zhuǎn)換為異步模式
    this.asyncContext.addListener(this); // 這里要求傳入的是一個(gè) AsyncListener 接口的實(shí)現(xiàn)類,接口內(nèi)定義了4個(gè)回調(diào)方法废恋。
    if (this.timeout != null) {
        this.asyncContext.setTimeout(this.timeout); // 整個(gè)異步請(qǐng)求的超時(shí)時(shí)間谈秫,由Servlet容器管理,超時(shí)后會(huì)回調(diào)超時(shí)的回調(diào)方法鱼鼓。
    }
}

public boolean isAsyncStarted() {
    // asyncContext 不為空代表請(qǐng)求已經(jīng)轉(zhuǎn)換為了異步模式拟烫。   
    // isAsyncStarted 則是檢查此請(qǐng)求是否已調(diào)用 startAsync 方法使其進(jìn)入異步模式。
    return (this.asyncContext != null && getRequest().isAsyncStarted());
}

1.5.4 AsyncListener接口的實(shí)現(xiàn)類

// 這個(gè)參數(shù)要記住迄本,因?yàn)樵谏厦娴脑创a中硕淑,有很多判斷這個(gè)的,這個(gè)是用來判斷異步請(qǐng)求是否已經(jīng)完成的嘉赎。
private AtomicBoolean asyncCompleted = new AtomicBoolean(false);

// 下面就是說的那4個(gè)回調(diào)函數(shù)

// StandardServletAsyncWebRequest
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
}

// StandardServletAsyncWebRequest
@Override
public void onError(AsyncEvent event) throws IOException {
    // 調(diào)用在 WebAsyncManager#startCallableProcessing 中添加的處理器
    this.exceptionHandlers.forEach(consumer -> consumer.accept(event.getThrowable()));
}

// StandardServletAsyncWebRequest
@Override
public void onTimeout(AsyncEvent event) throws IOException {
    // 調(diào)用在 WebAsyncManager#startCallableProcessing 中添加的處理器
    this.timeoutHandlers.forEach(Runnable::run);
}

// StandardServletAsyncWebRequest
@Override
public void onComplete(AsyncEvent event) throws IOException {
    // 調(diào)用在 WebAsyncManager#startCallableProcessing 中添加的處理器
    this.completionHandlers.forEach(Runnable::run);

    this.asyncContext = null; // 當(dāng)請(qǐng)求執(zhí)行完成后置媳,這里對(duì) asyncContext 清空以契合之前的邏輯
    this.asyncCompleted.set(true); // 重點(diǎn)記憶
}

在了解了這些方法的基礎(chǔ)上進(jìn)行一些較為重要的補(bǔ)充
如果超時(shí),觸發(fā)順序是onTimeout->onComplete曹阔。
手動(dòng)調(diào)用AsyncContext#complete()半开,觸發(fā)的順序是onComplete
手動(dòng)調(diào)用AsyncContext#dispatch()赃份,觸發(fā)的順序是dispatch->onComplete寂拆。
這里的dispatch指的是對(duì)當(dāng)前請(qǐng)求的URI再次調(diào)度。
而且dispatch發(fā)生時(shí)使用的線程和AsyncListener回調(diào)使用的線程是同一個(gè)。

記住這些內(nèi)容在理解下文時(shí)很重要。

1.5.5 WebAsyncManager#setConcurrentResultAndDispatch

// WebAsyncManager
private void setConcurrentResultAndDispatch(Object result) {
    synchronized (WebAsyncManager.this) {
        // concurrentResult 代表最終的處理結(jié)果器予,如果 != RESULT_NONE萌衬,則代表已經(jīng)處理過異步請(qǐng)求結(jié)果了。
        if (this.concurrentResult != RESULT_NONE) { 
            return;
        }
        this.concurrentResult = result;
    }

    // 如果異步請(qǐng)求已經(jīng)執(zhí)行完成了的哑子,就直接返回,可以看到它訪問的就是我們上面剛剛看到過的 asyncCompleted。
    // 防止對(duì)同一個(gè)請(qǐng)求的結(jié)果進(jìn)行多次處理炭序。
    if (this.asyncWebRequest.isAsyncComplete()) {
        if (logger.isDebugEnabled()) {
            logger.debug("Async result set but request already complete: " + formatRequestUri());
        }
        return;
    }

    if (logger.isDebugEnabled()) {
        boolean isError = result instanceof Throwable;
        logger.debug("Async " + (isError ? "error" : "result set") + ", dispatch to " + formatRequestUri());
    }
    // 間接調(diào)用 AsyncContext#dispatch 來對(duì)當(dāng)前請(qǐng)求的URI再次調(diào)度 (意思就是會(huì)對(duì)當(dāng)前URI再發(fā)送一個(gè)請(qǐng)求)。
    this.asyncWebRequest.dispatch();
}

// StandardServletAsyncWebRequest
public boolean isAsyncComplete() {
    return this.asyncCompleted.get();
}

isAsyncComplete的工作邏輯
由于AsyncContext#dispatch()會(huì)導(dǎo)致再次調(diào)度當(dāng)前請(qǐng)求的URI苍日,
加上上面也說過的惭聂,再次調(diào)度時(shí)使用的線程與回調(diào)方法時(shí)使用的線程是同一個(gè),
所以asyncCompleted的值相恃,對(duì)當(dāng)前是準(zhǔn)確有效的辜纲,這就是isAsyncComplete的工作邏輯。
asyncCompleted表示了當(dāng)前異步請(qǐng)求是否正常執(zhí)行通過,所以在上面很多地方都有使用isAsyncComplete()進(jìn)行判斷耕腾。

1.5.6 執(zhí)行完成的邏輯

基于上面這些知識(shí)點(diǎn)见剩,這里在梳理一下處理完成的邏輯
第一步:Spring MVC 檢測到某個(gè)請(qǐng)求處理方法的返回值是Callable類型的返回值扫俺。
第二步:選用CallableMethodReturnValueHandler作為返回值處理器苍苞。
第三步:將當(dāng)前請(qǐng)求轉(zhuǎn)為異步模式,同時(shí)注冊(cè)一個(gè)自定義監(jiān)聽器牵舵。
第四步:在當(dāng)前請(qǐng)求內(nèi)創(chuàng)建一個(gè)線程任務(wù)柒啤,隨后提交給taskExecutor調(diào)度,然后這個(gè)線程任務(wù)順利執(zhí)行完成畸颅。
第五步:隨著AsyncContext#dispatch()方法的調(diào)用担巩,Servlet容器會(huì)再次調(diào)度當(dāng)前請(qǐng)求的URI,然后回調(diào)我們監(jiān)聽器內(nèi)部的onComplete(AsyncEvent)函數(shù)没炒。
第六步:在onComplete(AsyncEvent)內(nèi)調(diào)用執(zhí)行完成后的處理器涛癌,執(zhí)行攔截器的afterCompletion方法。

第四步里的這個(gè)線程任務(wù)內(nèi)主要包含的邏輯有:
1.執(zhí)行請(qǐng)求處理方法返回的 Callable.
2.調(diào)用`AsyncContext#dispatch()`送火,通知Servlet容器對(duì)請(qǐng)求進(jìn)行再調(diào)度拳话。

截至到第四步這里,這些步驟都是在首次請(qǐng)求的線程中進(jìn)行的業(yè)務(wù)邏輯种吸。
從第五步開始弃衍,之后的步驟都是在新的線程內(nèi)完成的。

1.5.7 執(zhí)行超時(shí)的邏輯

基于上面這些知識(shí)點(diǎn)坚俗,這里在梳理一下超時(shí)的邏輯镜盯。
第一步:Spring MVC 檢測到某個(gè)請(qǐng)求處理方法的返回值是Callable類型的返回值
第二步:選用CallableMethodReturnValueHandler作為返回值處理器猖败。
第三步:將當(dāng)前請(qǐng)求轉(zhuǎn)為異步模式速缆,同時(shí)注冊(cè)一個(gè)自定義監(jiān)聽器。
第四步:在當(dāng)前請(qǐng)求內(nèi)創(chuàng)建一個(gè)線程任務(wù)恩闻,隨后提交給taskExecutor調(diào)度艺糜,但由于Callable執(zhí)行過久導(dǎo)致請(qǐng)求超時(shí)。
第五步:Servlet容器會(huì)再次調(diào)度當(dāng)前請(qǐng)求的URI幢尚,然后首先回調(diào)我們監(jiān)聽器內(nèi)部的onTimeout(AsyncEvent)破停,然后調(diào)用onComplete(AsyncEvent)
第六步:在onTimeout(AsyncEvent)內(nèi)調(diào)用執(zhí)行超時(shí)后的處理器,在onComplete(AsyncEvent)內(nèi)調(diào)用執(zhí)行完成后的處理器尉剩。

執(zhí)行超時(shí)的邏輯 和 執(zhí)行完成的邏輯辱挥,它們的差別點(diǎn)開始于第四步的 Callable 能否在指定時(shí)間內(nèi)執(zhí)行完成。
能完成就代表異步操作已完成边涕;完不成就代表異步操作已超時(shí);

這里主要看下超時(shí)機(jī)制的處理邏輯。
FutureTask API文檔

// WebAsyncManager#startCallableProcessing 代碼片段
Future<?> future = this.taskExecutor.submit(() -> {
    result = callable.call();
    // ... 線程任務(wù)內(nèi)的邏輯
});
interceptorChain.setTaskFuture(future);

// WebAsyncManager#startCallableProcessing 代碼片段
this.asyncWebRequest.addTimeoutHandler(() -> {
    logger.debug("Async request timeout for " + formatRequestUri());
    Object result = interceptorChain.triggerAfterTimeout(this.asyncWebRequest, callable);
    if (result != CallableProcessingInterceptor.RESULT_NONE) {
        setConcurrentResultAndDispatch(result);
    }
});

// CallableInterceptorChain
public Object triggerAfterTimeout(NativeWebRequest request, Callable<?> task) {
    // 這里會(huì)調(diào)用Future#cancel功蜓,試圖取消任務(wù)的執(zhí)行园爷。
    cancelTask();

    // 調(diào)用攔截器的超時(shí)方法,對(duì)返回值進(jìn)行組裝式撼,默認(rèn)情況下 result 會(huì)是一個(gè) AsyncRequestTimeoutException 類型的異常童社。
    // 可以看 TimeoutCallableProcessingInterceptor 這個(gè)攔截器得知原因。
    for (CallableProcessingInterceptor interceptor : this.interceptors) {
        try {
            Object result = interceptor.handleTimeout(request, task);
            if (result == CallableProcessingInterceptor.RESPONSE_HANDLED) {
                break;
            }
            else if (result != CallableProcessingInterceptor.RESULT_NONE) {
                return result;
            }
        }
        catch (Throwable ex) {
            return ex;
        }
    }
    return CallableProcessingInterceptor.RESULT_NONE;
}

// CallableInterceptorChain
private void cancelTask() {
    Future<?> future = this.taskFuture;
    if (future != null) {
        try {
            // 會(huì)嘗試取消這個(gè)任務(wù)的執(zhí)行著隆。
            // 沒什么好說的扰楼,已經(jīng)在上面放了 API 文檔,自己看美浦。
            future.cancel(true);
        }
        catch (Throwable ex) {
            // Ignore
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末弦赖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子浦辨,更是在濱河造成了極大的恐慌蹬竖,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件流酬,死亡現(xiàn)場離奇詭異币厕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)芽腾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門旦装,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人摊滔,你說我怎么就攤上這事阴绢。” “怎么了惭载?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵旱函,是天一觀的道長。 經(jīng)常有香客問我描滔,道長棒妨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任含长,我火速辦了婚禮券腔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拘泞。我一直安慰自己纷纫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布陪腌。 她就那樣靜靜地躺著辱魁,像睡著了一般烟瞧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上染簇,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天参滴,我揣著相機(jī)與錄音,去河邊找鬼锻弓。 笑死砾赔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的青灼。 我是一名探鬼主播暴心,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼杂拨!你這毒婦竟也來了专普?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤扳躬,失蹤者是張志新(化名)和其女友劉穎脆诉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贷币,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡击胜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了役纹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偶摔。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖促脉,靈堂內(nèi)的尸體忽然破棺而出辰斋,到底是詐尸還是另有隱情,我是刑警寧澤瘸味,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布宫仗,位于F島的核電站,受9級(jí)特大地震影響旁仿,放射性物質(zhì)發(fā)生泄漏藕夫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一枯冈、第九天 我趴在偏房一處隱蔽的房頂上張望毅贮。 院中可真熱鬧,春花似錦尘奏、人聲如沸滩褥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瑰煎。三九已至铺然,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丢间,已是汗流浹背探熔。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烘挫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓柬甥,卻偏偏與公主長得像饮六,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苛蒲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容