SpringMVC 4.3 源碼分析之 HandlerMethodReturnValueHandler

1. HandlerMethodReturnValueHandler 概述

HandlerMethodReturnValueHandler = HandlerMethod(@RequestMapping的處理器) + returnValue(返回值) + Handler(處理器), 其實(shí)就是 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;
}

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

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

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

3. 處理異步返回值的 HandlerMethodReturnValueHandler

這一類(lèi)接口都是 AsyncHandlerMethodReturnValueHandler 的子類(lèi), 主要如下:

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

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

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

主流程的代碼如下:

protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    Object outputValue;                         // 返回值內(nèi)容
    Class<?> valueType;                         // 返回值類(lèi)型
    Type declaredType;
    if (value instanceof CharSequence) {        // 若 value 是 Char
        outputValue = value.toString();         // 設(shè)置 outputValue
        valueType = String.class;               // 設(shè)置 返回值的類(lèi)型
        declaredType = String.class;
    } else {
        outputValue = value;                    // 設(shè)置返回值
        valueType = getReturnValueType(outputValue, returnType); // 獲取 返回值的類(lèi)型
        declaredType = getGenericType(returnType); // 返回值類(lèi)型
    }
    HttpServletRequest request = inputMessage.getServletRequest();
    // 獲取 request 對(duì)應(yīng)的 MediaType(PS: ContentNegotiationManager 進(jìn)行獲取 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 找出 兩個(gè) 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 進(jìn)行排序
    MediaType.sortBySpecificityAndQuality(mediaTypes);
    MediaType selectedMediaType = null;
    // 篩選出其中一個(gè) 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, 進(jìn)行返回值的轉(zhuǎn)換處理 下面進(jìn)行選擇 converter 時(shí), 加入 MediaType 作為考慮
        for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
            // 下面分類(lèi) HttpMessageConverter 進(jìn)行分開(kāi)處理 <-- 先是支持 Type 的 HttpMessageConverter
            if (messageConverter instanceof GenericHttpMessageConverter) {
                if (((GenericHttpMessageConverter) messageConverter).canWrite(declaredType, valueType, selectedMediaType)) {
                    // 通過(guò) ResponseBodyAdvice 在將數(shù)據(jù)寫(xiě)入輸出流之前進(jìn)行一些增強(qiáng)操作
                    outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
                    if (outputValue != null) {
                        // 對(duì) Http 請(qǐng)求中的 header 進(jìn)行一些處理
                        addContentDispositionHeader(inputMessage, outputMessage);
                        // HttpMessageConverter 進(jìn)行處理, 將數(shù)據(jù)寫(xiě)入 outputStream, 并且寫(xiě)到遠(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)) {
                // 通過(guò) ResponseBodyAdvice 在將數(shù)據(jù)寫(xiě)入輸出流之前進(jìn)行一些增強(qiáng)操作
                outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
                if (outputValue != null) {
                    // 對(duì) Http 請(qǐng)求中的 header 進(jìn)行一些處理
                    addContentDispositionHeader(inputMessage, outputMessage);
                    // HttpMessageConverter 進(jìn)行處理, 將數(shù)據(jù)寫(xiě)入 outputStream, 并且寫(xiě)到遠(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);
}

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

5. HandlerMethodReturnValueHandler 總結(jié)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辣恋,一起剝皮案震驚了整個(gè)濱河市亮垫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伟骨,老刑警劉巖饮潦,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異携狭,居然都是意外死亡继蜡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)逛腿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)稀并,“玉大人,你說(shuō)我怎么就攤上這事单默〉饩伲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵雕凹,是天一觀的道長(zhǎng)殴俱。 經(jīng)常有香客問(wèn)我政冻,道長(zhǎng),這世上最難降的妖魔是什么线欲? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任明场,我火速辦了婚禮,結(jié)果婚禮上李丰,老公的妹妹穿的比我還像新娘苦锨。我一直安慰自己,他們只是感情好趴泌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布舟舒。 她就那樣靜靜地躺著,像睡著了一般嗜憔。 火紅的嫁衣襯著肌膚如雪秃励。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天吉捶,我揣著相機(jī)與錄音夺鲜,去河邊找鬼。 笑死呐舔,一個(gè)胖子當(dāng)著我的面吹牛币励,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播珊拼,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼食呻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了澎现?” 一聲冷哼從身側(cè)響起仅胞,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昔头,沒(méi)想到半個(gè)月后饼问,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡揭斧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了峻堰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讹开。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捐名,靈堂內(nèi)的尸體忽然破棺而出旦万,到底是詐尸還是另有隱情,我是刑警寧澤镶蹋,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布成艘,位于F島的核電站赏半,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏淆两。R本人自食惡果不足惜断箫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秋冰。 院中可真熱鬧仲义,春花似錦、人聲如沸剑勾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)行剂。三九已至,卻和暖如春嫩海,著一層夾襖步出監(jiān)牢的瞬間捂刺,已是汗流浹背谣拣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叠萍,地道東北人芝发。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像苛谷,于是被迫代替她去往敵國(guó)和親辅鲸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355