自定义MessageConverter【原创】

自定义MessageConverter

简介

HttpMessageConverter是用来处理request和reponse里的数据的。spring内置了大量的HttpMessageConvert,例如。MappingJackson2HttpMessageConverter、StringHttpMessageConverter等。本篇文章只要讲自定义的HttpMessageConvert,并注册 HttpMessageConverter到SpringMVC。

代码示例

1、自定义HttpMessageConverter

import com.ch4.domain.DemoObj;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
​
import java.io.IOException;
import java.nio.charset.Charset;
​
/**
 * @description: 自定义MessageConverter
 *
 * @author: Shenshuaihu
 * @version: 1.0
 * @data: 2019-06-24 23:31
 */
public class MyMessageConverter extends AbstractHttpMessageConverter<DemoObj> {
​
 /**
 * 自定义的媒体类型 application/x-wisely
 */
 public MyMessageConverter() {
 super(new MediaType("application", "x-wisely", Charset.forName("UTF-8")));
 }
​
 /**
 * HttpMessageConverter 只处理 DemoObj类
 * @param aClass
 * @return
 */
 @Override
 protected boolean supports(Class<?> aClass) {
 return DemoObj.class.isAssignableFrom(aClass);
 }
​
 /**
 * 重写 readInternal 处理请求数据 。此处由‘-’隔开的数据,转化DemoObj对象
 * @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[0]), tempArr[1]);
 }
​
 /**
 *  重写writeInternal 处理出如何输出数据到response 此处在原样中输出加上“hello”
 * @param obj
 * @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());
 }
}

继承了AbstractHttpMessageConverter,重写了里面的几个方法,readInternal ,处理请求数据 。此处由‘-’隔开的数据,转化DemoObj对象;writeInternal 处理出如何输出数据到response 此处在原样中输出加上“hello”; 自定义的媒体类型 application/x-wisely

2、配置自定义的HttpMessageConverter的Bean

需要在Spring MVC里注册 HttpMessageConverter 两个方法

 /**
 * 添加一个自定义的HttpMessageConverter,不会覆盖默认注册的HttpMessageConverter
 * @param converters
 */
 @Override
 public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
 converters.add(converter());
 }
​
 /**
 * 自定义的bean注册进来
 * @return
 */
 @Bean
 public MyMessageConverter converter() {
 return new MyMessageConverter();
 }

extendMessageConverters 仅添加一个自定义的 HttpMessageConverter,不会覆盖默认的HttpMessageConverter。

configureMessageConverters 方法 重载会覆盖掉SpringMVC 默认注册的多个HttpMessageConverter

3、 控制器ConverterController

import com.ch4.domain.DemoObj;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
​
/**
 * @description:
 *
 * @author: Shenshuaihu
 * @version: 1.0
 * @data: 2019-06-25 08:37
 */
​
@Controller
public class ConverterController {
​
 @RequestMapping(value = "/convert", produces = {"application/x-wisely"})
 public @ResponseBody DemoObj converter(@RequestBody DemoObj demoObj) {
 return demoObj;
 }
}

这是是接受一个 DemoObj 类型的参数并返回,实际上用参入字符串格式然后通过HttpMessageConverter中的readInternal 解析为 dto,并使用HttpMessageConverter中的writeInternal 处理输出!

简单的DTO DemoObj

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
​
/**
 * @description:  用于获取request对象参数和返回此对象到response
 *
 * @author: Shenshuaihu
 * @version: 1.0
 * @data: 2019-06-15 10:49
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DemoObj {
 private Long id;
 private String name;
}

4、页面 converter.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
 <title>Spting-Code</title>
 <link rel="stylesheet" type="text/css" value="">
</head>
<title>converter page</title>
<body>
<h2> converter.jsp </h2>
​
<div id="resp"></div>
<input type="button" onclick="req();" value="请求"/> <br/>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
​
<script>
 function req() {
 $.ajax({
 url: "convert",
 data: "1-ssh",
 type: "POST",
 contentType: "application/x-wisely",
 success: function (data) {
 console.log("data :" + data)
 $("#resp").html(data);
 }
 })
 }
</script>
</body>
</html>

传入服务器的数据是 1-ssh 会被处理为DTO 媒体类型是 application/x-wisely

5、演示效果图

![json调用链.png](https://upload-images.jianshu.io/upload_images/16573347-c19438c0abe3d2b5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

源码j简单分析

org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.java

同意是定义数据类型,在转格式

// Json 格式数据
 public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
 super(objectMapper, new MediaType[]{new MediaType("application", "json", DEFAULT_CHARSET), new MediaType("application", "*+json", DEFAULT_CHARSET)});
 }

protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
 if (this.jsonPrefix != null) {
 generator.writeRaw(this.jsonPrefix);
 }
​
 String jsonpFunction = object instanceof MappingJacksonValue ? ((MappingJacksonValue)object).getJsonpFunction() : null;
 if (jsonpFunction != null) {
 generator.writeRaw(jsonpFunction + "(");
 }
​
 }
​
​
 public void writeRaw(String text) throws IOException {
 int len = text.length();
 int room = this._outputEnd - this._outputTail;
 if (room == 0) {
 this._flushBuffer();
 room = this._outputEnd - this._outputTail;
 }
​
 if (room >= len) {
 text.getChars(0, len, this._outputBuffer, this._outputTail);
 this._outputTail += len;
 } else {
 this.writeRawLong(text);
 }
​
 }

在AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters方法里调用HttpMessageConverter处理消息

 protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
 MediaType contentType;
 try {
 contentType = inputMessage.getHeaders().getContentType();
 } catch (InvalidMediaTypeException var10) {
 throw new HttpMediaTypeNotSupportedException(var10.getMessage());
 }
​
 if (contentType == null) {
 contentType = MediaType.APPLICATION_OCTET_STREAM;
 }
​
 Class<?> contextClass = methodParam.getContainingClass();
 Class<T> targetClass = ResolvableType.forMethodParameter(methodParam, targetType).resolve(Object.class);
 Iterator var7 = this.messageConverters.iterator();
​
 HttpMessageConverter converter;
 do {
 if (!var7.hasNext()) {
 throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
 }
​
 converter = (HttpMessageConverter)var7.next();
 if (converter instanceof GenericHttpMessageConverter) {
 GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter)converter;
 if (genericConverter.canRead(targetType, contextClass, contentType)) {
 if (this.logger.isDebugEnabled()) {
 this.logger.debug("Reading [" + targetType + "] as \"" + contentType + "\" using [" + converter + "]");
 }
​
 return genericConverter.read(targetType, contextClass, inputMessage);
 }
 }
 } while(!converter.canRead(targetClass, contentType));
​
 if (this.logger.isDebugEnabled()) {
 this.logger.debug("Reading [" + targetClass.getName() + "] as \"" + contentType + "\" using [" + converter + "]");
 }
​
 return converter.read(targetClass, inputMessage);
 }

主要就是取出所有的HttpMessageConverter,然后调用read方法读取消息,再处理MediaType,返回body。 在拦截器链执行后,HttpMessageConverter处理的消息就是返回的http消息了。

json调用链.png

MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter

abstract class AbstractJackson2HttpMessageConverter extends AbstractHttpMessageConverter<Object> implements GenericHttpMessageConverter<Object>

abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T>


public interface HttpMessageConverter<T> {
    boolean canRead(Class<?> var1, MediaType var2);

    boolean canWrite(Class<?> var1, MediaType var2);

    List<MediaType> getSupportedMediaTypes();

    T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;

    void write(T var1, MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}

参考内容:
Spring源码分析(五) MappingJackson2HttpMessageConverter

2019/07/01 晚于成都

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容