中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringMVC中如何修改返回值類型后的消息轉換器處理方式

發布時間:2021-09-24 11:29:50 來源:億速云 閱讀:199 作者:小新 欄目:開發技術

這篇文章給大家分享的是有關SpringMVC中如何修改返回值類型后的消息轉換器處理方式的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

問題案例

假設有一個Controller方法如下

@RequestMapping(value = "test")
    @ResponseBody
    public Object test() {
        Map<String,String> param = new HashMap<>();
        param.put("name","userwyh");
        return param;
    }

然后我們通過實現ResponseBodyAdvice接口對返回值再輸出之前進行了修改,此處我們把它變成了String類型,直接返回hello,world。

@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        return "hello,world";
    }
}

這樣不管Controller中的test方法返回什么值,我們都會把它變成hello,world輸出。想想也沒有什么不對,仔細確認了代碼,它就是這樣做的。

于是,啟動項目,打開瀏覽器,地址欄輸入localhost:8080/test,回車。

我們確實看到了hello,world字樣,沒有任何問題。

SpringMVC中如何修改返回值類型后的消息轉換器處理方式

但是仔細一看的話,你會發現hello,world前后都多了一個引號。這顯然不是我們想要的返回值啊!!!

為什么?

這時候標題提到的SpringMVC的消息轉換器HttpMessageConverter就該出場了。

HttpMessageConverter源碼剖析可以移步 SpringMVC源碼剖析-消息轉換器HttpMessageConverter 進行查看。我們這里就不對源碼進行詳細的解讀了。

首先SpringMVC會加載在spring-servlet.xml配置好的消息轉換器到messageConverters里。

protected final List<HttpMessageConverter<?>> messageConverters;

debug時發現SpringMVC不止加載了我們配置好的消息轉換器,它還加載了另外7個默認的消息轉換器,即便7個之中你在配置文件中配置了,它依然會再次加載一次。如圖,0和1是配置的,2-8是默認加載的。

SpringMVC中如何修改返回值類型后的消息轉換器處理方式

上圖中的方法writeWithMessageConverters就是在Controller方法執行之后就進入的,在抽象類AbstractMessageConverterMethodProcessor的第164行處。這個方法也正是SpringMVC為當前返回值選擇合適的消息轉換器,選擇的順序就是messageConverters的轉換器順序。

SpringMVC中如何修改返回值類型后的消息轉換器處理方式

通過閱讀源碼,我們知道,此處對messageConverters進行了遍歷,先判斷當前的轉換器對當前返回類型是否能寫canWrite,如果能得話就會調用beforeBodyWrite方法,然后把beforeBodyWrite的返回值通過write方法進行輸出。如果不能的話就選擇下一個轉換器。如果最終沒有一個合適的,就會拋出一個異常。

了解問題原因及分析

有了上面對HttpMessageConverter的簡單描述,我們大概可以得到一個結論:

因為在Controller中的返回值類型是java.util.HashMap,所以在writeWithMessageConverters方法中SpringMVC選定的轉換器并不是StringHttpMessageConverter,而是MappingJackson2HttpMessageConverter。

我們可以通過在MyResponseBodyAdvice類beforeBodyWrite方法中打印參數得以證明確實當前SpringMVC選擇的轉換器就是MappingJackson2HttpMessageConverter。

然后我們在beforeBodyWrite執行返回了String類型的hello,world。而此時選定的轉換器已經是MappingJackson2HttpMessageConverter了,所以通過該轉換器進行轉換輸出。

我們再通過一個實例說明MappingJackson2HttpMessageConverter會把String前后新增雙引號。

SpringMVC中如何修改返回值類型后的消息轉換器處理方式

通過上面的分析,相信大家已經大概知道問題的來龍去脈了。

所以第一個反應當然就是重寫MappingJackson2HttpMessageConverter的某個方法咯。

不過在這之前,我們還需要對SpringMVC的源碼進行進一步分析。

上面提到它會把beforeBodyWrite的返回值通過write方法進行輸出,所以我們需要了解這個write方法。它是一個接口,由具體的消息轉換器進行實現。SpringMVC自己提供了一個抽象類AbstractGenericHttpMessageConverter進行了實現,但把具體的write任務交給了抽象方法writeInternal。如下圖

SpringMVC中如何修改返回值類型后的消息轉換器處理方式

接下來就是看MappingJackson2HttpMessageConverter的代碼了。該類的父類AbstractJackson2HttpMessageConverter確實繼承了AbstractGenericHttpMessageConverter并實現了writeInternal方法。

SpringMVC中如何修改返回值類型后的消息轉換器處理方式

所以,方法很簡單,我們只需要把jackson的writeInternal重寫一下就可以了。

解決方法

1、創建一個MappingJackson2HttpMessageConverterFactory類

package com.userwyh.spring.controller;
import org.slf4j.Logger;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import static org.slf4j.LoggerFactory.getLogger;
/**
 * Created by userwyh on 2017/3/4.
 */
public class MappingJackson2HttpMessageConverterFactory {
    private static final Logger logger = getLogger(MappingJackson2HttpMessageConverterFactory.class);
    public MappingJackson2HttpMessageConverter init() {
        return new MappingJackson2HttpMessageConverter(){
            /**
             * 重寫Jackson消息轉換器的writeInternal方法
             * SpringMVC選定了具體的消息轉換類型后,會調用具體類型的write方法,將Java對象轉換后寫入返回內容
             */
            @Override
            protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
                if (object instanceof String){
                    logger.info("在MyResponseBodyAdvice進行轉換時返回值變成String了,不能用原來選定消息轉換器進行轉換,直接使用StringHttpMessageConverter轉換");
                    //StringHttpMessageConverter中就是用以下代碼寫的
                    Charset charset = this.getContentTypeCharset(outputMessage.getHeaders().getContentType());
                    StreamUtils.copy((String)object, charset, outputMessage.getBody());
                }else{
                    logger.info("返回值不是String類型,還是使用之前選擇的轉換器進行消息轉換");
                    super.writeInternal(object, type, outputMessage);
                }
            }
            private Charset getContentTypeCharset(MediaType contentType) {
                return contentType != null && contentType.getCharset() != null?contentType.getCharset():this.getDefaultCharset();
            }
        };
    }
}

2、稍微修改一下spring的配置文件

<mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>image/jpeg</value>
                        <value>image/png</value>
                        <value>image/gif</value>
                    </list>
                </property>
            </bean>
            <bean  factory-bean="mappingJackson2HttpMessageConverterFactory" factory-method="init"
                   class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" >
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <bean id="mappingJackson2HttpMessageConverterFactory" class = "com.userwyh.spring.controller.MappingJackson2HttpMessageConverterFactory" />

此時SpringMVC啟動時,messageConverters的順序就是ByteArrayHttpMessageConverter,mappingJackson2HttpMessageConverterFactory,然后另外7個默認的,共9個。如第一張截圖所示即為配置后的效果。

3、啟動項目,打開瀏覽器,地址欄輸入localhost:8080/test,回車。雙引號沒有了,正是我們想要的結果。

SpringMVC中如何修改返回值類型后的消息轉換器處理方式

再看一下日志:

三月 05, 2017 11:01:51 下午 com.userwyh.spring.controller.MappingJackson2HttpMessageConverterFactory$1 writeInternal 信息: 在MyResponseBodyAdvice進行轉換時返回值變成String了,不能用原來選定消息轉換器進行轉換,直接使用StringHttpMessageConverter轉換

結語

其實你可以直接在Controller中直接返回String類型的?

其實你可以針對在MyResponseBodyAdvice 中確認要返回不同類型的,直接在Controller中判斷下就行了啊,比如以下這樣就可以了,為什么要這么麻煩呢?

@RequestMapping(value = "test")
    @ResponseBody
    public Object test() {
        Map<String,String> param = new HashMap<>();
        param.put("name","userwyh");
        if(condition){
            return "hello,world";
        }
        return param;
    }

可能,因為喜歡折騰,既然可以在MyResponseBodyAdvice 進行統一的返回值轉換,我就斷定可以找到方法解決這個問題的,也確實解決了。

感謝各位的閱讀!關于“SpringMVC中如何修改返回值類型后的消息轉換器處理方式”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

铁岭市| 西充县| 象山县| 阿图什市| 额济纳旗| 洪洞县| 凤山县| 陆河县| 灵丘县| 磐石市| 长武县| 梅河口市| 页游| 镶黄旗| 旺苍县| 安新县| 芜湖市| 资溪县| 广水市| 科技| 博乐市| 左云县| 姜堰市| 连州市| 夹江县| 谢通门县| 长治市| 固阳县| 顺平县| 达日县| 长宁县| 龙海市| 汪清县| 绥化市| 正安县| 云霄县| 甘德县| 农安县| 永福县| 澎湖县| 泌阳县|