HttpMessageConverter那回事
相信使用过Spring的开发人员都用过@RequestBody、@ResponseBody注解,可以直接将输入解析成Json、将输出解析成Json,但HTTP 请求和响应是基于文本的,意味着浏览器和服务器通过交换原始文本进行通信,而这里其实就是HttpMessageConverter发挥着作用。
HttpMessageConverter
Http请求响应报文其实都是字符串,当请求报文到java程序会被封装为一个ServletInputStream流,开发人员再读取报文,响应报文则通过ServletOutputStream流,来输出响应报文。
从流中只能读取到原始的字符串报文,同样输出流也是。那么在报文到达SpringMVC / SpringBoot和从SpringMVC / SpringBoot出去,都存在一个字符串到java对象的转化问题。这一过程,在SpringMVC / SpringBoot中,是通过HttpMessageConverter来解决的。HttpMessageConverter接口源码:
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, MediaType mediaType);
boolean canWrite(Class<?> clazz, MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
下面以一例子来说明:
@RequestMapping("/test")
@ResponseBody
public String test(@RequestBody String param) {
return "param '" + param + "'";
}
在请求进入test方法前,会根据@RequestBody注解选择对应的HttpMessageConverter实现类来将请求参数解析到param变量中,因为这里的参数是String类型的,所以这里是使用了StringHttpMessageConverter类,它的canRead()方法返回true,然后read()方法会从请求中读出请求参数,绑定到test()方法的param变量中。
同理当执行test方法后,由于返回值标识了@ResponseBody,SpringMVC / SpringBoot将使用StringHttpMessageConverter的write()方法,将结果作为String值写入响应报文,当然,此时canWrite()方法返回true。
借用下图简单描述整个过程:

在Spring的处理过程中,一次请求报文和一次响应报文,分别被抽象为一个请求消息HttpInputMessage和一个响应消息HttpOutputMessage。
处理请求时,由合适的消息转换器将请求报文绑定为方法中的形参对象,在这里同一个对象就有可能出现多种不同的消息形式,如json、xml。同样响应请求也是同样道理。
在Spring中,针对不同的消息形式,有不同的HttpMessageConverter实现类来处理各种消息形式,至于各种消息解析实现的不同,则在不同的HttpMessageConverter实现类中。
替换@ResponseBody默认的HttpMessageConverter
这里使用SpringBoot演示例子,在SpringMVC / SpringBoot中@RequestBody这类注解默认使用的是jackson来解析json,看下面例子:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testt")
@ResponseBody
public User testt() {
User user = new User("name", 18);
return user;
}
}
public class User {
private String username;
private Integer age;
private Integer phone;
private String email;
public User(String username, Integer age) {
super();
this.username = username;
this.age = age;
}
}
浏览器访问/user/testt返回如下:

这就是使用jackson解析的结果,现在来改成使用fastjson解析对象,这里就是替换默认的HttpMessageConverter,就是将其改成使用FastJsonHttpMessageConverter来处理Java对象与HttpInputMessage/HttpOutputMessage间的转化。
首先新建一配置类来添加配置FastJsonHttpMessageConverter,Spring4.x开始推荐使用Java配置加注解的方式,也就是无xml文件,SpringBoot就更是了。
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import java.nio.charset.Charset;
@Configuration
public class HttpMessageConverterConfig {
//引入Fastjson解析json,不使用默认的jackson
//必须在pom.xml引入fastjson的jar包,并且版必须大于1.2.10
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
//1、定义一个convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//2、添加fastjson的配置信息
FastJsonConfig fastJsonConfig = new FastJsonConfig();
SerializerFeature[] serializerFeatures = new SerializerFeature[]{
// 输出key是包含双引号
// SerializerFeature.QuoteFieldNames,
// 是否输出为null的字段,若为null 则显示该字段
// SerializerFeature.WriteMapNullValue,
// 数值字段如果为null,则输出为0
SerializerFeature.WriteNullNumberAsZero,
// List字段如果为null,输出为[],而非null
SerializerFeature.WriteNullListAsEmpty,
// 字符类型字段如果为null,输出为"",而非null
SerializerFeature.WriteNullStringAsEmpty,
// Boolean字段如果为null,输出为false,而非null
SerializerFeature.WriteNullBooleanAsFalse,
// Date的日期转换器
SerializerFeature.WriteDateUseDateFormat,
// 循环引用
SerializerFeature.DisableCircularReferenceDetect,
};
fastJsonConfig.setSerializerFeatures(serializerFeatures);
fastJsonConfig.setCharset(Charset.forName("UTF-8"));
//3、在convert中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
//4、将convert添加到converters中
HttpMessageConverter<?> converter = fastConverter;
return new HttpMessageConverters(converter);
}
}
这里将字符串类型的值如果是null就返回“”,数值类型的如果是null就返回0,重启应用,再次访问/user/testt接口,返回如下:

可以看到此时null都转化成“”或0了。
HttpMessageConverter那回事的更多相关文章
- 写了cookie阻止通过输入地址直接访问下一个html,但是直接输入地址访问时,会闪一下下一个页面,怎么回事啊????、
描述:做了两个页面login.html index.html 在index的body加了onload事件,调用一个js,js中有cookie的判断,防止没有登录就打开index.html,如果没 ...
- 图文详解Unity3D中Material的Tiling和Offset是怎么回事
图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔 ...
- 关于OATUH中的AUTHRAZITON CODE和TOKEN的关系,实际上就是这么回事
关于OATUH中的AUTHRAZITON CODE和TOKEN的关系,实际上就是这么回事 每回要拿AUTHRAZITON CODE换取TOKEN,然后才能正常通信, 为什么要多一步呢?直接给TOKEN ...
- c语言中,既然不支持函数重载,那么printf算怎么回事?在c语言中,它不就是被重载了吗?
这个问题问的不错.其实printf不是重载,c语言不支持函数重载 这句话是对的.printf函数是通过变长参数表实现的.你可以查看一下printf的函数原型声明.printf函数的实现在不同的机器上是 ...
- no suitable HttpMessageConverter found for request type [java.lang.Integer]
今天在使用Spring Template的时候遇到了这个异常: no suitable HttpMessageConverter found for request type [java.lang.I ...
- HTTP Error 403没有了,但是中文全都是乱码。又是怎么回事?
首先是简单的网页抓取程序: [python] import sys, urllib2req = urllib2.Request("http://blog.csdn.net/nevasun&q ...
- (1)as_view() (2)在urls.py里面出现的pk是怎么回事 (3)RetrieveAPIView表示什么
下面的代码都是我从github上下载的源码中摘取的django: https://github.com/django/django 下载命令: git clone https://github.com ...
- MySQL的数据库无法插入中文是怎么回事?
插入中文就报错: Incorrect string value: '\xE7\x8F\xBD\xE7\x8F\xBA' for column 'name' at row 1 用set names ut ...
- 再次讲解js中的回收机制是怎么一回事。
在前几天的一篇闭包文章中我们简单的介绍了一下闭包,但是并没有深入的讲解,因为闭包涉及的知识点比较多,为了能够更好的理解闭包,今天讲解一下关于js中的回收机制. 在初识闭包一文中我说过js中有回收机制这 ...
随机推荐
- 第8.19节 使用__doc__访问Python文档字符串(DocStrings )
__doc__特殊变量用于查看类.函数.模块的帮助信息,这些帮助信息存放在文档字符串中. 一. 关于文档字符串 关于文档字符串前面很多章节提到过,DocStrings 文档字符串用于程序的文档说明,并 ...
- Gif开发笔记(一):gif介绍、编译和工程模板
前言 实现gif图片的解码和生成. Gif 简介 GIF格式的名称是Graphics Interchange Format的缩写,是在1987年由Compu Serve公司为了填补跨平 ...
- MyBatis if 标签的坑,居然被我踩到了。。。
事件的原因是这样的,需求是按条件查数据然后给前端展示就行了,写的时候想着挺简单的,不就是使用 MyBatis 动态 SQL 去查询数据吗? 现实还是很残酷的,等我写完上完 UAT 后,前端同学说根据s ...
- 题解 CF1446D2 【Frequency Problem (Hard Version)】
给出一个跑得快一点的做法,洛谷最优解 (时间是第二名的 \(\frac{1}{2}\)), CF 第一页 D1 首先找到整个序列的众数 \(G\), 很容易证明答案序列中的两个众数中其中一个是 \(G ...
- TMOOC 1969 开锁
update on 2020.2.28 时隔近日重新想这道题,其实复杂度正确的解法是 可持久化 01 Trie. 考虑对于每一个 \(a[i]\),考虑能将它作为最大值的最大包容区间 \([l, r] ...
- AcWing 204. 表达整数的奇怪方式 / Strange Way To Express Integers
我作为一个初中蒟蒻,听y大视频听了5遍还不懂,快哭了.然后终于(好像)搞懂,写成题解加深一下记忆... 将式子等价转换 对于每两个式子(我们考虑将其合并): \(x \equiv a_1 \%\ m_ ...
- 【学习笔记】K-D tree 区域查询时间复杂度简易证明
查询算法的流程 如果查询与当前结点的区域无交集,直接跳出. 如果查询将当前结点的区域包含,直接跳出并上传答案. 有交集但不包含,继续递归求解. K-D Tree 如何划分区域 可以借助下文图片理解. ...
- windows10 64位下安装oracle 11g和PL/SQL Developer
一.材料准备: oracle11g安装包(64位) oracle11g客户端(32位) PL\SQL Developer安装包(32位) 1.下载Oracle 11g链接:http://www.ora ...
- ElasticSearch中head插件的简单用法
1.首先在左侧打开Query栏. 2.Query下方的第一栏是分别输入es的地址.端口号.index.type. 3.Query下方的第二栏是输入将要执行的方式,旁边的下拉框是辅助选择执行的类型,如图 ...
- 2020-2021-1 20209307《Linux内核原理与分析》第三周作业
一.计算机的三大法宝 存储程序计算机.函数调用堆栈机制.中断机制 二.堆栈 堆栈的作用:记录函数调用框架.传递函数参数.保存返回值的地址.提供局部变量存储空间 堆栈操作:push栈顶地址减少四个字节. ...