1.简介:

Spring在处理请求时,由合适的消息转换器将请求报文绑定为方法中的形参对象,在这里,同一个对象就有可能出现多种不同的消息形式,比如json和xml。同样,当响应请求时,方法的返回值也同样可能被返回为不同的消息形式,比如json和xml。

在Spring中,针对不同的消息形式,我们有不同的HttpMessageConverter实现类来处理各种消息形式。但是,只要这些消息所蕴含的“有效信息”是一致的,那么各种不同的消息转换器,都会生成同样的转换结果。至于各种消息间解析细节的不同,就被屏蔽在不同的HttpMessageConverter实现类中了。

2.应用:

方法一:

SpringBoot中很多配置都使用默认的,但是如果你自己手动配置了,那么容器就是使用你自己的配置

自定义消息转化器,只需要在@Configuration的类中添加消息转化器的@bean加入到Spring容器,就会被Spring Boot自动加入到容器中。

@Configuration
public class FastJsonHttpMessageConverterConfig { @Bean
public HttpMessageConverters fastJsonHttpMessageConverters(){
//1.需要定义一个convert转换消息的对象;
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
//2:添加fastJson的配置信息;
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
//3处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
//4.在convert中添加配置信息.
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = fastJsonHttpMessageConverter;
return new HttpMessageConverters(converter);
}
}

方法二:

在继承WebMvcConfigurerAdapter的类中重写(覆盖)configureMessageConverters方法

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /**
* Created by qhong on 2018/6/1 10:59
**/
@ControllerAdvice
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter { @Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
ObjectMapper objectMapper = builder.build();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);// 忽略 transient 修饰的属性
converters.add(new MappingJackson2HttpMessageConverter(objectMapper)); //字符串转换器
//StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
//converters.add(converter); //FastJson转换器
// //1.需要定义一个convert转换消息的对象;
// FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
// //2.添加fastJson的配置信息,比如:是否要格式化返回的json数据;
// FastJsonConfig fastJsonConfig = new FastJsonConfig();
// fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
// //3处理中文乱码问题
// List<MediaType> fastMediaTypes = new ArrayList<>();
// fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
// //4.在convert中添加配置信息.
// fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
// fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
// //5.将convert添加到converters当中.
// converters.add(fastJsonHttpMessageConverter); super.configureMessageConverters(converters);
} @Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
System.out.println("Converters:Begin");
System.out.println("num:"+converters.size());
for (HttpMessageConverter<?> messageConverter : converters) {
System.out.println(messageConverter);
}
System.out.println("Converters:End");
}
}

方法三:

使用extendMessageConverters方法,其实在上一个code中已经贴出来,只是用来查看总共有那些消息转换器的,通过上面的,可以查看的自己手动添加的消息转换器。

    @Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.clear();
converters.add(new FastJsonHttpMessageConverter());
}

3.原理

我们知道,Http请求和响应报文本质上都是一串字符串,当请求报文来到java世界,它会被封装成为一个ServletInputStream的输入流,供我们读取报文。响应报文则是通过一个ServletOutputStream的输出流,来输出响应报文。

我们从流中,只能读取到原始的字符串报文,同样,我们往输出流中,也只能写原始的字符。而在java世界中,处理业务逻辑,都是以一个个有业务意义的对象为处理维度的,那么在报文到达SpringMVC和从SpringMVC出去,都存在一个字符串到java对象的阻抗问题。这一过程,不可能由开发者手工转换。我们知道,在Struts2中,采用了OGNL来应对这个问题,而在SpringMVC中,它是HttpMessageConverter机制。

package org.springframework.http.converter;

import java.io.IOException;
import java.util.List; import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType; 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; }

HttpMessageConverter接口的定义出现了成对的canRead(),read()和canWrite(),write()方法,MediaType是对请求的Media Type属性的封装。举个例子,当我们声明了下面这个处理方法。

@RequestMapping(value="/query", method=RequestMethod.POST)
public @ResponseBody User queryUser(@RequestBody String tel) {
return query(tel);
}

在SpringMVC进入queryUser方法前,会根据@RequestBody注解选择适当的HttpMessageConverter实现类来将请求参数解析到string变量中,具体来说是使用了StringHttpMessageConverter类,它的canRead()方法返回true,然后它的read()方法会从请求中读出请求参数,绑定到readString()方法的string变量中。

当SpringMVC执行readString方法后,由于返回值标识了@ResponseBody,SpringMVC将使用MappingJackson2HttpMessageConverter(或者自定义的FastJsonHttpMessageConverter)的write()方法,将结果转换成json字符串写入响应报文,当然,此时canWrite()方法返回true。

我们可以用下面的图,简单描述一下这个过程。

上面只是利用一些第三方的消息转换器

自定义消息转换器:

捡到网上一个注释比较全的。

public class MyMessageConverter extends AbstractHttpMessageConverter<DemoObj> {

    public MyMessageConverter() {
//x-zyf 是自定义的媒体类型
super(new MediaType("application", "x-zyf", Charset.forName("Utf-8")));
} @Override
protected boolean supports(Class<?> aClass) {
//表示只支持DemoObj这个类
//return DemoObj.class.isAssignableFrom(aClass);
//返回false则不会支持任何类,要想使用,就需要返回true
return true;
} /**
* 重写readInternal方法
* 处理请求中的数据
*
* @param aClass
* @param httpInputMessage
* @return
* @throws IOException
* @throws HttpMessageNotReadableException
*/
@Override
protected DemoObj readInternal(Class<? extends DemoObj> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
//获得请求中的数据,得到字符串形式
String temp = StreamUtils.copyToString(httpInputMessage.getBody(), Charset.forName("UTF-8")); //前端请求的格式是我们自己约定的
String[] tempArr = temp.split("-"); return new DemoObj(new Long(tempArr[]), tempArr[]);
} /**
* 重写writeInternal方法
* 处理任何输出数据到response
*
* @param obj 要输出到response的对象
* @param httpOutputMessage
* @throws IOException
* @throws HttpMessageNotWritableException
*/
@Override
protected void writeInternal(DemoObj obj, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
String out = "hello:" + obj.getId() + "-" + obj.getName();
httpOutputMessage.getBody().write(out.getBytes());
}
}

这里特别要注意的就是support,特么的,我以为返回false就是不进行消息转换呢,原来不是。

readInternal:

public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConverter<Object> {

    public static final ObjectMapper mapper = new ObjectMapper();

    public static final Logger LOG = LoggerFactory.getLogger(MappingJackson2HttpMessageConverter.class);

    private boolean encryptFlag = false;

    public void setEncryptFlag(boolean encryptFlag) {
this.encryptFlag = encryptFlag;
} public MappingJackson2HttpMessageConverter() {
super(new MediaType("application", "json", Charset.forName("UTF-8")));
} protected boolean supports(Class<?> clazz) {
return true;
} protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return mapper.readValue(inputMessage.getBody(), clazz);
} protected void writeInternal(Object d, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
byte[] jsonBytes;
if(d.getClass()!=HuishiUserLoginResponse.class) {
BasicRes r = new BasicRes();
if (d != null && d != SysConstant.NULL_RESPONSE) {
LOG.info("====>>>>> 响应数据:response={}", d.toString());
if (encryptFlag) {
try {
long repStart = System.currentTimeMillis();
String json = ToJson(d);
String data = CoderUtil.encryptAES(json);
r.setData(data);
long repEnd = System.currentTimeMillis();
long repTime = repEnd - repStart;
logger.info("加密耗时==>" + repTime + " ms");
} catch (Exception e) {
e.printStackTrace();
} } else {
r.setData(d);
}
}
jsonBytes = ToJsonAsBytes(r);
}else{
jsonBytes = ToJsonAsBytes(d);
} OutputStream out = outputMessage.getBody();
out.write(jsonBytes, , jsonBytes.length);
out.close();
} public static byte[] ToJsonAsBytes(Object value) {
try {
return mapper.writeValueAsBytes(value);
} catch (Exception var2) {
LOG.error("ToJsonAsBytes error,Object:{}", value, var2);
return null;
}
} public static String ToJson(Object value) {
try {
return mapper.writeValueAsString(value);
} catch (Exception var2) {
LOG.error("ToJosn error,Object:{}", value, var2);
return "";
}
}
}

https://www.jianshu.com/p/ffe56d9553fd

https://www.cnblogs.com/hellxz/p/8735602.html

https://blog.csdn.net/mickjoust/article/details/51671060

https://www.cnblogs.com/page12/p/8166935.html

https://my.oschina.net/lichhao/blog/172562

https://blog.csdn.net/L_Sail/article/details/70209845

https://blog.csdn.net/linFeng_csdn/article/details/72835451

SpringBoot 消息转换器 HttpMessageConverter的更多相关文章

  1. 【Spring学习笔记-MVC-1.3】消息转换器HttpMessageConverter

    作者:ssslinppp       参考链接: SpringMVC源码剖析(五)-消息转换器HttpMessageConverter: http://my.oschina.net/lichhao/b ...

  2. springboot自定义消息转换器HttpMessageConverter

    在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制就是利用HttpMessageCo ...

  3. SpringMVC——消息转换器HttpMessageConverter(转)

    文章转自http://blog.csdn.net/cq1982/article/details/44101293 概述 在SpringMVC中,可以使用@RequestBody和@ResponseBo ...

  4. SpringMVC源码剖析(五)-消息转换器HttpMessageConverter

    原文链接:https://my.oschina.net/lichhao/blog/172562 #概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分 ...

  5. SpringMVC源码剖析5:消息转换器HttpMessageConverter与@ResponseBody注解

    转自 SpringMVC关于json.xml自动转换的原理研究[附带源码分析] 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Spring源码 ...

  6. springboot自定义消息转换器HttpMessageConverter Spring Boot - 使用Gson替换Jackson

    Jackson一直是springframework默认的json库,从4.1开始,springframework支持通过配置GsonHttpMessageConverter的方式使用Gson. 在典型 ...

  7. HandlerMethodArgumentResolver(三):基于消息转换器的参数处理器【享学Spring MVC】

    每篇一句 一个事实是:对于大多数技术,了解只需要一天,简单搞起来只需要一周.入门可能只需要一个月 前言 通过 前面两篇文章 的介绍,相信你对HandlerMethodArgumentResolver了 ...

  8. SpringMVC自定义配置消息转换器踩坑总结

    问题描述 最近在开发时候碰到一个问题,springmvc页面向后台传数据的时候,通常我是这样处理的,在前台把数据打成一个json,在后台接口中使用@requestbody定义一个对象来接收,但是这次数 ...

  9. Spring Boot项目中如何定制HTTP消息转换器

    在构建RESTful数据服务过程中,我们定义了controller.repositories,并用一些注解修饰它们,但是到现在为止我们还没执行过对象的转换--将java实体对象转换成HTTP的数据输出 ...

随机推荐

  1. 十 js中forEach,for in,for of循环的用法

    一.一般的遍历数组的方法: var array = [1,2,3,4,5,6,7]; for (var i = 0; i < array.length; i++) { console.log(i ...

  2. 9.if/else/elif

    简单的条件是通过使用 if/else/elif 语法创建的.条件的括号是允许的,但不是必需的.考虑到基于表的缩进的性质,可以使用 elif 而不是 else/if 来维持缩进的级别. if [expr ...

  3. web前端名词

    HTML: HyperText Markup Language      超文本标记语言 XHTML:Extensible HyperText Markup Language   可扩展性超文本标记语 ...

  4. Vue系列之 => 自定义键盘修饰符

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Spark学习之路 (十二)SparkCore的调优之资源调优

    摘抄自:https://tech.meituan.com/spark-tuning-basic.html 一.概述 在开发完Spark作业之后,就该为作业配置合适的资源了.Spark的资源参数,基本都 ...

  6. Python 构造一个可接受任意数量参数的函数

    为了能让一个函数接受任意数量的位置参数,可以使用一个* 参数 在这个例子中,rest 是由所有其他位置参数组成的元组.然后我们在代码中把它当成了一个序列来进行后续的计算

  7. Jmeter自己jar包的引用

    1.编写清空指定文件夹里所有内容的jar包 package org.na;import java.io.File;public class deletedir {    public static b ...

  8. 大数据权限管理工具 Apache Ranger 初识

    资料参考: Apache Ranger – Introduction http://ranger.apache.org/ 阿里云 Ranger简介 Apache Ranger初识 - 阿里云 大数据权 ...

  9. 转:C#判断ContextMenuStrip右键菜单的来源(从哪个控件弹出来的)

    转载自:http://hi.baidu.com/cookiemulan/item/82df8ff867dd53cc531c26c7 有时候,为了提高性能和节约资源,我们会为多个控件,指定同一个右键弹出 ...

  10. 以太坊智能合约介绍,Solidity介绍

    以太坊智能合约介绍,Solidity介绍 一个简单的智能合约 先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节. Storage contract SimpleSt ...