- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章SpringMVC修改返回值类型后的消息转换器处理方式由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
o(╯□╰)o这标题看起来有点奇怪,所以先以一个小小的案例来说明一下本文要描述和解决的问题 。
假设有一个Controller方法如下 。
@RequestMapping(value = "test") @ResponseBody public Object test() { Map<String,String> param = new HashMap<>(); param.put("name","userwyh"); return param; }
然后我们通过实现ResponseBodyAdvice接口对返回值再输出之前进行了修改,此处我们把它变成了String类型,直接返回hello,world.
@ControllerAdvicepublic 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字样,没有任何问题.
但是仔细一看的话,你会发现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是默认加载的.
上图中的方法writeWithMessageConverters就是在Controller方法执行之后就进入的,在抽象类AbstractMessageConverterMethodProcessor的第164行处。这个方法也正是SpringMVC为当前返回值选择合适的消息转换器,选择的顺序就是messageConverters的转换器顺序.
通过阅读源码,我们知道,此处对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前后新增双引号.
通过上面的分析,相信大家已经大概知道问题的来龙去脉了.
所以第一个反应当然就是重写MappingJackson2HttpMessageConverter的某个方法咯.
不过在这之前,我们还需要对SpringMVC的源码进行进一步分析.
上面提到它会把beforeBodyWrite的返回值通过write方法进行输出,所以我们需要了解这个write方法。它是一个接口,由具体的消息转换器进行实现。SpringMVC自己提供了一个抽象类AbstractGenericHttpMessageConverter进行了实现,但把具体的write任务交给了抽象方法writeInternal。如下图 。
接下来就是看MappingJackson2HttpMessageConverter的代码了。该类的父类AbstractJackson2HttpMessageConverter确实继承了AbstractGenericHttpMessageConverter并实现了writeInternal方法.
所以,方法很简单,我们只需要把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,回车。双引号没有了,正是我们想要的结果.
再看一下日志:
三月 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 进行统一的返回值转换,我就断定可以找到方法解决这个问题的,也确实解决了.
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://blog.csdn.net/userwyh/article/details/60480364 。
最后此篇关于SpringMVC修改返回值类型后的消息转换器处理方式的文章就讲到这里了,如果你想了解更多关于SpringMVC修改返回值类型后的消息转换器处理方式的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试编写一个相当多态的库。我遇到了一种更容易表现出来却很难说出来的情况。它看起来有点像这样: {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE
谁能解释一下这个表达式是如何工作的? type = type || 'any'; 这是否意味着如果类型未定义则使用“任意”? 最佳答案 如果 type 为“falsy”(即 false,或 undef
我有一个界面,在IAnimal.fs中, namespace Kingdom type IAnimal = abstract member Eat : Food -> unit 以及另一个成功
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: What is the difference between (type)value and type(va
在 C# 中,default(Nullable) 之间有区别吗? (或 default(long?) )和 default(long) ? Long只是一个例子,它可以是任何其他struct类型。 最
假设我有一个案例类: case class Foo(num: Int, str: String, bool: Boolean) 现在我还有一个简单的包装器: sealed trait Wrapper[
这个问题在这里已经有了答案: Create C# delegate type with ref parameter at runtime (1 个回答) 关闭 2 年前。 为了即时创建委托(dele
我正在尝试获取图像的 dct。一开始我遇到了错误 The function/feature is not implemented (Odd-size DCT's are not implemented
我正在尝试使用 AFNetworking 的 AFPropertyListRequestOperation,但是当我尝试下载它时,出现错误 预期的内容类型{( “应用程序/x-plist” )}, 得
我在下面收到错误。我知道这段代码的意思,但我不知道界面应该是什么样子: Element implicitly has an 'any' type because index expression is
我尝试将 SignalType 从 ReactiveCocoa 扩展为自定义 ErrorType,代码如下所示 enum MyError: ErrorType { // .. cases }
我无法在任何其他问题中找到答案。假设我有一个抽象父类(super class) Abstract0,它有两个子类 Concrete1 和 Concrete1。我希望能够在 Abstract0 中定义类
我想知道为什么这个索引没有用在 RANGE 类型中,而是用在 INDEX 中: 索引: CREATE INDEX myindex ON orders(order_date); 查询: EXPLAIN
我正在使用 RxJava,现在我尝试通过提供 lambda 来订阅可观察对象: observableProvider.stringForKey(CURRENT_DELETED_ID) .sub
我已经尝试了几乎所有解决问题的方法,其中包括。为 提供类型使用app.use(express.static('public'))还有更多,但我似乎无法为此找到解决方案。 index.js : imp
以下哪个 CSS 选择器更快? input[type="submit"] { /* styles */ } 或 [type="submit"] { /* styles */ } 只是好
我不知道这个设置有什么问题,我在 IDEA 中获得了所有注释(@Controller、@Repository、@Service),它在行号左侧显示 bean,然后转到该 bean。 这是错误: 14-
我听从了建议 registering java function as a callback in C function并且可以使用“简单”类型(例如整数和字符串)进行回调,例如: jstring j
有一些 java 类,加载到 Oracle 数据库(版本 11g)和 pl/sql 函数包装器: create or replace function getDataFromJava( in_uLis
我已经从 David Walsh 的 css 动画回调中获取代码并将其修改为 TypeScript。但是,我收到一个错误,我不知道为什么: interface IBrowserPrefix { [
我是一名优秀的程序员,十分优秀!