相信使用过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。

借用下图简单描述整个过程:

 
00001.png

在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返回如下:

 
00002.png

这就是使用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接口,返回如下:

 
00003.png

可以看到此时null都转化成“”或0了。

HttpMessageConverter那回事的更多相关文章

  1. 写了cookie阻止通过输入地址直接访问下一个html,但是直接输入地址访问时,会闪一下下一个页面,怎么回事啊????、

    描述:做了两个页面login.html   index.html  在index的body加了onload事件,调用一个js,js中有cookie的判断,防止没有登录就打开index.html,如果没 ...

  2. 图文详解Unity3D中Material的Tiling和Offset是怎么回事

    图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔 ...

  3. 关于OATUH中的AUTHRAZITON CODE和TOKEN的关系,实际上就是这么回事

    关于OATUH中的AUTHRAZITON CODE和TOKEN的关系,实际上就是这么回事 每回要拿AUTHRAZITON CODE换取TOKEN,然后才能正常通信, 为什么要多一步呢?直接给TOKEN ...

  4. c语言中,既然不支持函数重载,那么printf算怎么回事?在c语言中,它不就是被重载了吗?

    这个问题问的不错.其实printf不是重载,c语言不支持函数重载 这句话是对的.printf函数是通过变长参数表实现的.你可以查看一下printf的函数原型声明.printf函数的实现在不同的机器上是 ...

  5. no suitable HttpMessageConverter found for request type [java.lang.Integer]

    今天在使用Spring Template的时候遇到了这个异常: no suitable HttpMessageConverter found for request type [java.lang.I ...

  6. HTTP Error 403没有了,但是中文全都是乱码。又是怎么回事?

    首先是简单的网页抓取程序: [python] import sys, urllib2req = urllib2.Request("http://blog.csdn.net/nevasun&q ...

  7. (1)as_view() (2)在urls.py里面出现的pk是怎么回事 (3)RetrieveAPIView表示什么

    下面的代码都是我从github上下载的源码中摘取的django: https://github.com/django/django 下载命令: git clone https://github.com ...

  8. MySQL的数据库无法插入中文是怎么回事?

    插入中文就报错: Incorrect string value: '\xE7\x8F\xBD\xE7\x8F\xBA' for column 'name' at row 1 用set names ut ...

  9. 再次讲解js中的回收机制是怎么一回事。

    在前几天的一篇闭包文章中我们简单的介绍了一下闭包,但是并没有深入的讲解,因为闭包涉及的知识点比较多,为了能够更好的理解闭包,今天讲解一下关于js中的回收机制. 在初识闭包一文中我说过js中有回收机制这 ...

随机推荐

  1. PyQt开发案例:结合QDial实现的QStackedWidget堆叠窗口程序例子及完整代码

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.案例说明 本案例是老猿在学习QStackedWidget中的一个测试案例,该案例使用QStack ...

  2. PyQt(Python+Qt)学习随笔:QTreeWidget树型部件中的QTreeWidgetItem项构造方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTreeWidget树型部件的项是单独的类对象,这个类就是QTreeWidgetItem. QTr ...

  3. PyQt Designer中连接信号和槽时为什么只能连接控件自己的信号和槽函数?

    老猿在学习ListView组件时,想实现一个在ListView组件中选中一个选择项后触发消息给主窗口,通过主窗口显示当前选中的项的内容. 进入QtDesigner后,设计一个图形界面,其中窗口界面使用 ...

  4. 【系统设计】不同分类的商品动态添加扩展属性的方法(WMS、小型电商)

    在做公司WMS系统的时候,遇到了一个商品模块的设计问题,具体业务流程如下. 客户提供需要存放的商品清单,根据商品清单生成收货单给客户,然后生成入库单进行商品入库操作. 在生成这两个单之前首先要录入商品 ...

  5. 小齐读者拿到快手、百度、网易等 offer 的独门秘籍!

    小齐说: 这篇文章来自读者冰红茶,他刚结束了秋招,拿了很多家 offer. 和他聊完之后,我觉得他的备考思路也完全适用于美国的面试,只是分值要调整一下,但是具体每一块的内容,地球村通用.所以分享给大家 ...

  6. MySQL事务(一)认识事务

    简单来说,事务就是要保证一组数据库操作,要么全部完成,要么全部失败. 为什么要有事务 数据库中的数据是共享资源,因此数据库系统通常要支持多个用户的或不同应用程序的访问,会出现并发存取数据的现象. 数据 ...

  7. Vue组件化开发(原有项目的改造)

    将组件定义成一个js和css,然后供其他页面进行调用 demo.html <!DOCTYPE html> <html> <head> <meta charse ...

  8. eureka配置说明

    以下配置来源于jar中的源码,采用标准两个空格作为缩进 eureka.yml spring: application: name: APPLICATION_NAME # 应用名,在eureka上注册为 ...

  9. Scrapy+Scrapyd+Scrapydweb实现爬虫可视化

    Scrapy+Scrapyd+Scrapydweb实现爬虫可视化 Scrapyd是一个服务,用来运行scrapy爬虫的 它允许你部署你的scrapy项目以及通过HTTP JSON的方式控制你的爬虫 官 ...

  10. Python 学习笔记 之 02 - 高级特性总结

    切片 语法:  li.[x:y:z]  li为list.tuple等数据类型,x为开始进行切片的位置,y为切片停止的位置(不包含y),z为xy切片后的结果里,每间隔z个元素输出一次结果.  x默认为0 ...