最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringMVC修改返回值類型后的消息轉(zhuǎn)換器處理方式

 更新時間:2021年09月23日 15:37:01   作者:userwyh  
這篇文章主要介紹了SpringMVC修改返回值類型后的消息轉(zhuǎn)換器處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

o(╯□╰)o這標(biāo)題看起來有點(diǎn)奇怪,所以先以一個小小的案例來說明一下本文要描述和解決的問題

問題案例

假設(shè)有一個Controller方法如下

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

然后我們通過實(shí)現(xiàn)ResponseBodyAdvice接口對返回值再輸出之前進(jìn)行了修改,此處我們把它變成了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輸出。想想也沒有什么不對,仔細(xì)確認(rèn)了代碼,它就是這樣做的。

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

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

這里寫圖片描述

但是仔細(xì)一看的話,你會發(fā)現(xiàn)hello,world前后都多了一個引號。這顯然不是我們想要的返回值?。。。?/p>

為什么?

這時候標(biāo)題提到的SpringMVC的消息轉(zhuǎn)換器HttpMessageConverter就該出場了。

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

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

protected final List<HttpMessageConverter<?>> messageConverters;

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

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

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

了解問題原因及分析

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

因?yàn)樵贑ontroller中的返回值類型是java.util.HashMap,所以在writeWithMessageConverters方法中SpringMVC選定的轉(zhuǎn)換器并不是StringHttpMessageConverter,而是MappingJackson2HttpMessageConverter。

我們可以通過在MyResponseBodyAdvice類beforeBodyWrite方法中打印參數(shù)得以證明確實(shí)當(dāng)前SpringMVC選擇的轉(zhuǎn)換器就是MappingJackson2HttpMessageConverter。

然后我們在beforeBodyWrite執(zhí)行返回了String類型的hello,world。而此時選定的轉(zhuǎn)換器已經(jīng)是MappingJackson2HttpMessageConverter了,所以通過該轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換輸出。

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

這里寫圖片描述

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

所以第一個反應(yīng)當(dāng)然就是重寫MappingJackson2HttpMessageConverter的某個方法咯。

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

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

接下來就是看MappingJackson2HttpMessageConverter的代碼了。該類的父類AbstractJackson2HttpMessageConverter確實(shí)繼承了AbstractGenericHttpMessageConverter并實(shí)現(xiàn)了writeInternal方法。

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

解決方法

1、創(chuàng)建一個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消息轉(zhuǎn)換器的writeInternal方法
             * SpringMVC選定了具體的消息轉(zhuǎn)換類型后,會調(diào)用具體類型的write方法,將Java對象轉(zhuǎn)換后寫入返回內(nèi)容
             */
            @Override
            protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
                if (object instanceof String){
                    logger.info("在MyResponseBodyAdvice進(jìn)行轉(zhuǎn)換時返回值變成String了,不能用原來選定消息轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換,直接使用StringHttpMessageConverter轉(zhuǎn)換");
                    //StringHttpMessageConverter中就是用以下代碼寫的
                    Charset charset = this.getContentTypeCharset(outputMessage.getHeaders().getContentType());
                    StreamUtils.copy((String)object, charset, outputMessage.getBody());
                }else{
                    logger.info("返回值不是String類型,還是使用之前選擇的轉(zhuǎn)換器進(jìn)行消息轉(zhuǎn)換");
                    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個默認(rèn)的,共9個。如第一張截圖所示即為配置后的效果。

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

這里寫圖片描述

再看一下日志:

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

結(jié)語

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

其實(shí)你可以針對在MyResponseBodyAdvice 中確認(rèn)要返回不同類型的,直接在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;
    }

可能,因?yàn)橄矚g折騰,既然可以在MyResponseBodyAdvice 進(jìn)行統(tǒng)一的返回值轉(zhuǎn)換,我就斷定可以找到方法解決這個問題的,也確實(shí)解決了。

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 利用Java實(shí)現(xiàn)簡單的猜數(shù)字小游戲

    利用Java實(shí)現(xiàn)簡單的猜數(shù)字小游戲

    這篇文章主要為大家詳細(xì)介紹了如何利用java語言實(shí)現(xiàn)猜數(shù)字小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 地址到經(jīng)緯度坐標(biāo)轉(zhuǎn)化的JAVA代碼

    地址到經(jīng)緯度坐標(biāo)轉(zhuǎn)化的JAVA代碼

    這篇文章介紹了地址到經(jīng)緯度坐標(biāo)轉(zhuǎn)化的JAVA代碼,有需要的朋友可以參考一下
    2013-09-09
  • SpringBoot實(shí)現(xiàn)動態(tài)數(shù)據(jù)源切換的項(xiàng)目實(shí)踐

    SpringBoot實(shí)現(xiàn)動態(tài)數(shù)據(jù)源切換的項(xiàng)目實(shí)踐

    在實(shí)際開發(fā)過程中,我們經(jīng)常遇到需要同時操作多個數(shù)據(jù)源的情況,本文主要介紹了SpringBoot實(shí)現(xiàn)動態(tài)數(shù)據(jù)源切換的項(xiàng)目實(shí)踐,具有一定的參考價值,感興趣的可以了解一下
    2024-04-04
  • Java中如何快速構(gòu)建項(xiàng)目腳手架的實(shí)現(xiàn)

    Java中如何快速構(gòu)建項(xiàng)目腳手架的實(shí)現(xiàn)

    這篇文章主要介紹了Java中如何快速構(gòu)建項(xiàng)目腳手架,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Spring Boot 快速搭建微服務(wù)框架詳細(xì)教程

    Spring Boot 快速搭建微服務(wù)框架詳細(xì)教程

    SpringBoot是為了簡化Spring應(yīng)用的創(chuàng)建、運(yùn)行、調(diào)試、部署等而出現(xiàn)的,使用它可以做到專注于Spring應(yīng)用的開發(fā),而無需過多關(guān)注XML的配置。本文重點(diǎn)給大家介紹Spring Boot 快速搭建微服務(wù)框架詳細(xì)教程,需要的的朋友參考下吧
    2017-09-09
  • IntelliJ?IDEA?2021.3永久最新激活至2099年(親測有效)

    IntelliJ?IDEA?2021.3永久最新激活至2099年(親測有效)

    最新版idea2021.3已出來,很多網(wǎng)友迫不及待的要升級idea2021最新版,今天小編抽空給大家整理了一篇教程關(guān)于idea2021.3最新激活教程,本文以idea2021.2.3為例通過圖文并茂的形式給大家分享激活詳細(xì)過程,感興趣的朋友參考下吧
    2020-12-12
  • JMETER用戶變量作用域測試流程

    JMETER用戶變量作用域測試流程

    這篇文章主要介紹了JMETER用戶變量作用域測試流程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • 使用java從亂碼文本中解析出正確的文本

    使用java從亂碼文本中解析出正確的文本

    這篇文章主要介紹了使用java從亂碼文本中解析出正確的文本的方法,需要的朋友可以參考下
    2014-04-04
  • MyBatis-Plus中的邏輯刪除使用詳解

    MyBatis-Plus中的邏輯刪除使用詳解

    開發(fā)系統(tǒng)時,有時候在實(shí)現(xiàn)功能時,刪除操作需要實(shí)現(xiàn)邏輯刪除就是將數(shù)據(jù)標(biāo)記為刪除,而并非真的物理刪除(非DELETE操作),查詢時需要攜帶狀態(tài)條件,確保被標(biāo)記的數(shù)據(jù)不被查詢到。這樣做的目的就是避免數(shù)據(jù)被真正的刪除
    2022-12-12
  • Spring MVC參數(shù)自動綁定List的解決方法

    Spring MVC參數(shù)自動綁定List的解決方法

    這篇文章主要為大家詳細(xì)介紹了Spring MVC參數(shù)自動綁定List的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12

最新評論

佳木斯市| 松滋市| 溧阳市| 缙云县| 玛曲县| 日照市| 沙田区| 黑龙江省| 津南区| 义乌市| 丰顺县| 汉阴县| 右玉县| 平潭县| 平乐县| 东兰县| 金湖县| 瑞安市| 松溪县| 安丘市| 双桥区| 米泉市| 香河县| 晋州市| 白水县| 沙湾县| 五指山市| 合山市| 肥城市| 宿迁市| 广平县| 且末县| 汉寿县| 德兴市| 漳平市| 达日县| 武冈市| 侯马市| 晴隆县| 南华县| 吉水县|