SpringMVC 4.3 源碼分析之 HandlerMethodReturnValueHandler

1. HandlerMethodReturnValueHandler 概述

HandlerMethodReturnValueHandler = HandlerMethod(@RequestMapping的處理器) + returnValue(返回值) + Handler(處理器), 其實就是 HandlerMethod 返滬值的處理器, 將返回值以特定的格式輸出到client端, 或?qū)?View 相關(guān)的信息設(shè)置到 ModelAndViewContainer 里面, 以便于視圖的渲染, 其接口如下:

//  HandlerMethod 的返回值處理器
public interface HandlerMethodReturnValueHandler {

    // 判斷當(dāng)前處理器是否支持 返回值
    boolean supportsReturnType(MethodParameter returnType);

    // 處理 HandlerMethod 的返回值
    void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

基于這個接口實現(xiàn)的處理器主要是如下幾類:

1. 返回值通常是View類型或 ViewName 或 Map 類型, 通常都是設(shè)置到 ModelAndViewContainer 里面
2. 支持返回值是 DeferredResult, ListenableFuture, CompletionStage 類型, 最后轉(zhuǎn)為對應(yīng)的適配器, 交給WebAsyncManager 進行運行
3. 根據(jù) ContentType 的類型將數(shù)據(jù)通過 HttpMessageConverter 轉(zhuǎn)換成對應(yīng)的數(shù)據(jù)格式, 最后輸出到遠(yuǎn)端
2. 處理 View 或 String 類型的HandlerMethodReturnValueHandler
1. ViewMethodReturnValueHandler
    針對 View 及其子類的返回值處理器, 主要還是將 View 設(shè)置到 ModelAndViewContainer
2. ViewNameMethodReturnValueHandler
    支持返回值為 CharSequence 類型, 設(shè)置 ModelAndViewContainer.setViewName
3. StreamingResponseBodyReturnValueHandler
    支持 StreamingResponseBody 類型的返回值, 最后將請求包裝成 Callable, 通過 Spring 的異步處理器 SimpleAsyncTaskExecutor 執(zhí)行 StreamingResponseBody 將數(shù)據(jù)流寫到遠(yuǎn)端
4. ModelMethodProcessor
    針對 Model 及其子類的返回值處理器, 主要還是將 ModelAndView 中的 model 設(shè)置到 ModelAndViewContainer
5. ModelAttributeMethodProcessor
    針對被 @ModelAttribute 修飾的返回值, 最后將數(shù)據(jù)寫入到 ModelAndViewContainer 中 
6. ModelAndViewResolverMethodReturnValueHandler
    通過 ModelAndViewResolver 將數(shù)據(jù)解析成 View, Model, 最后設(shè)置到 ModelAndViewContainer 里面
7. ModelAndViewMethodReturnValueHandler
    針對 ModelAndView 及其子類的返回值處理器, 主要還是將 ModelAndView 中的 status, model 設(shè)置到 ModelAndViewContainer
8. MapMethodProcessor
    支持返回值為 Map, 并將結(jié)果設(shè)置到 ModelAndViewContainer
9. HttpHeadersReturnValueHandler
    針對 HttpHeaders 類型的返回值, 最后設(shè)置到 Response 的 Header 中, 輸出到遠(yuǎn)端

上面幾個 ReturnValueHandler 基本上就是將數(shù)據(jù)設(shè)置到 ModelAndViewContainer 里面

3. 處理異步返回值的 HandlerMethodReturnValueHandler

這一類接口都是 AsyncHandlerMethodReturnValueHandler 的子類, 主要如下:

1. AsyncTaskMethodReturnValueHandler
    支持返回值是 WebAsyncTask 類型, 并通過 WebAsyncManager 中的 SimpleAsyncTaskExecutor 來進行處理
2. CallableMethodReturnValueHandler
    支持返回值是 Callable 類型, 并通過 WebAsyncManager 中的 SimpleAsyncTaskExecutor 來進行處理
3. CompletionStageReturnValueHandler
    支持返回值是 CompletionStage 類型, 并通過 WebAsyncManager 中的 SimpleAsyncTaskExecutor 來進行處理
4. DeferredResultMethodReturnValueHandler
    這個類適配 DeferredResult, ListenableFuture, CompletionStage 類型的返回值, 最后通過 WebAsyncManager 來完成任務(wù)的調(diào)用
5. ListenableFutureReturnValueHandler
    支持返回值是 ListenableFuture 類型, 并通過 WebAsyncManager 中的 SimpleAsyncTaskExecutor 來進行處理
4. 支持格式轉(zhuǎn)換的 HandlerMethodReturnValueHandler

這些 ReturnValueHandler 都是AbstractMessageConverterMethodProcessor的子類, 對應(yīng)實現(xiàn)的主邏輯也在 AbstractMessageConverterMethodProcessor 中

1. 獲取 request 對應(yīng)的 MediaType(PS: ContentNegotiationManager 進行獲取 Request 所能支持的 MediaType)
2. 返回 HttpMessageConverter 所支持的 MediaType
3. 若 producibleMediaType 是空 且 返回值有數(shù)據(jù), 直接拋出異常
4. 找出 producibleMediaType 與 AcceptMediaType 都兼容的 MediaType
5. 循環(huán)遍歷 HttpMessageConverter, 進行返回值的轉(zhuǎn)換處理 下面進行選擇 converter 時, 加入 MediaType 作為考慮
6. 通過 ResponseBodyAdvice 在將數(shù)據(jù)寫入輸出流之前進行一些增強操作
7. 對 Http 請求中的 header 進行一些配置信息進行處理
8. HttpMessageConverter 進行處理, 將數(shù)據(jù)寫入 outputStream, 并且寫到遠(yuǎn)端

主流程的代碼如下:

protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    Object outputValue;                         // 返回值內(nèi)容
    Class<?> valueType;                         // 返回值類型
    Type declaredType;
    if (value instanceof CharSequence) {        // 若 value 是 Char
        outputValue = value.toString();         // 設(shè)置 outputValue
        valueType = String.class;               // 設(shè)置 返回值的類型
        declaredType = String.class;
    } else {
        outputValue = value;                    // 設(shè)置返回值
        valueType = getReturnValueType(outputValue, returnType); // 獲取 返回值的類型
        declaredType = getGenericType(returnType); // 返回值類型
    }
    HttpServletRequest request = inputMessage.getServletRequest();
    // 獲取 request 對應(yīng)的 MediaType(PS: ContentNegotiationManager 進行獲取 Request 所能支持的 MediaType)
    List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
    // 返回 HttpMessageConverter 所支持的 MediaType
    List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
    // 若 producibleMediaType 是空 且 返回值有數(shù)據(jù), 直接拋出異常
    if (outputValue != null && producibleMediaTypes.isEmpty()) throw new IllegalArgumentException("No converter found for return value of type: " + valueType);

    Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
    // 找出兩端都兼容的 MediaType
    for (MediaType requestedType : requestedMediaTypes) {
        // getMostSpecificMediaType 找出 兩個 MediaType 中更加適合 producible
        for (MediaType producibleType : producibleMediaTypes) if (requestedType.isCompatibleWith(producibleType)) compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
    }
    if (compatibleMediaTypes.isEmpty()) {
        // 未出現(xiàn)兩者都兼容的 MediaType, 則直接拋出異常
        if (outputValue != null) throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
        return;
    }

    List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
    // 將所有的 MediaType 進行排序
    MediaType.sortBySpecificityAndQuality(mediaTypes);
    MediaType selectedMediaType = null;
    // 篩選出其中一個 mediaType
    for (MediaType mediaType : mediaTypes) {
        if (mediaType.isConcrete()) {
            selectedMediaType = mediaType;
            break;
        } else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
            selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; // 設(shè)置 application/octet-stream
            break;
        }
    }
    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        // 循環(huán)遍歷 HttpMessageConverter, 進行返回值的轉(zhuǎn)換處理 下面進行選擇 converter 時, 加入 MediaType 作為考慮
        for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
            // 下面分類 HttpMessageConverter 進行分開處理 <-- 先是支持 Type 的 HttpMessageConverter
            if (messageConverter instanceof GenericHttpMessageConverter) {
                if (((GenericHttpMessageConverter) messageConverter).canWrite(declaredType, valueType, selectedMediaType)) {
                    // 通過 ResponseBodyAdvice 在將數(shù)據(jù)寫入輸出流之前進行一些增強操作
                    outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
                    if (outputValue != null) {
                        // 對 Http 請求中的 header 進行一些處理
                        addContentDispositionHeader(inputMessage, outputMessage);
                        // HttpMessageConverter 進行處理, 將數(shù)據(jù)寫入 outputStream, 并且寫到遠(yuǎn)端
                        ((GenericHttpMessageConverter) messageConverter).write(outputValue, declaredType, selectedMediaType, outputMessage);
                        logger.info("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]");
                    }
                    return;
                }
            }
            // 普通 HttpMessageConverter 處理
            else if (messageConverter.canWrite(valueType, selectedMediaType)) {
                // 通過 ResponseBodyAdvice 在將數(shù)據(jù)寫入輸出流之前進行一些增強操作
                outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
                if (outputValue != null) {
                    // 對 Http 請求中的 header 進行一些處理
                    addContentDispositionHeader(inputMessage, outputMessage);
                    // HttpMessageConverter 進行處理, 將數(shù)據(jù)寫入 outputStream, 并且寫到遠(yuǎn)端
                    ((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);
                    logger.info("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]");
                }
                return;
            }
        }
    }
    if (outputValue != null) throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}

而子類 HttpEntityMethodProcessor, RequestResponseBodyMethodProcessor, 先準(zhǔn)備數(shù)據(jù), 然后使用父類的writeWithMessageConverters 方法, 將數(shù)據(jù)寫入數(shù)據(jù)流中, 最后在刷到遠(yuǎn)端!

5. HandlerMethodReturnValueHandler 總結(jié)

總體上 HandlerMethodReturnValueHandler 的設(shè)計體系和HandlerMethodArgumentResolver 差不多, 設(shè)計上夾雜著策略, 模版, 適配器 等等, 而我們常用的 @ResponseBody 就是通過RequestResponseBodyMethodProcessor 來解決的(PS: 根據(jù) Http中的 acceptMedia 與 HttpMessageConverter 來進行數(shù)據(jù)的轉(zhuǎn)換操作)!

最后編輯于
?著作權(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)容