自定义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 {
​
 /**
 * 自定义的媒体类型 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 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> 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" %>



 Spting-Code
 

converter page

converter.jsp


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

5、演示效果图

自定义MessageConverter【原创】_第1张图片
![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  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 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消息了。

自定义MessageConverter【原创】_第2张图片
json调用链.png

MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter

abstract class AbstractJackson2HttpMessageConverter extends AbstractHttpMessageConverter implements GenericHttpMessageConverter

abstract class AbstractHttpMessageConverter implements HttpMessageConverter


public interface HttpMessageConverter {
    boolean canRead(Class var1, MediaType var2);

    boolean canWrite(Class var1, MediaType var2);

    List getSupportedMediaTypes();

    T read(Class var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;

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

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

2019/07/01 晚于成都

你可能感兴趣的:(自定义MessageConverter【原创】)