自定义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 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> 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、演示效果图
源码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消息了。
MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter
abstract class AbstractJackson2HttpMessageConverter extends AbstractHttpMessageConverter
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 extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
void write(T var1, MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}
参考内容:
Spring源码分析(五) MappingJackson2HttpMessageConverter
2019/07/01 晚于成都