基于SpringBoot 2.2.0.RELEASE
先来看下HttpMessageConverter
接口的定义
public interface HttpMessageConverter<T> {
/**
* 表明是否该消息转化器可以被给出的class读取
* @param clazz 要测试可读性的类
* @param mediaType 要被读取的MediaType 如果未指定 为null
* 通常 这个值是请求头中的content-type
* @return 如果可读 返回true; 否则是false
*/
boolean canRead(Class<?> clazz, MediaType mediaType);
/**
* 表明是否该消息转化器可以写入给定的类
* @param clazz 要测试可写性的类
* @param mediaType 要被读取的MediaType 如果未指定 为null
* 通常 这个值是响应头中的Accept
* @return 如果可写 返回true; 否则是false
*/
boolean canWrite(Class<?> clazz, MediaType mediaType);
/**
* 返回这个消息转换器可以支持的MediaType的集合
* @return the list of supported media types
*/
List<MediaType> getSupportedMediaTypes();
/**
* 从给出的input message中读出给定类型的对象 然后返回
* @param clazz 返回对象的类型. 这个类必须支持前面的canRead方法且返回true
* @param inputMessage the HTTP input message to read from
* @return 被转换的对象
* @throws IOException in case of I/O errors
* @throws HttpMessageNotReadableException in case of conversion errors
*/
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
/**
* 将给定的对象写入给定的输出消息
* @param t 要写入输出消息的对象. 这个类型必须通过了前面的canWrite方法且返回true
* @param contentType 编写时使用的内容类型. 可能是{@code null}来表示必须使用转换器的默认内容类型。如 * 果不是{@code null},则此媒体类型必须具有以前传递给这个接口的{@link #canWrite canWrite}方法 该方 法必须具有返回{@code true}。
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
HttpMessageConverter接口分别有两组方法,分别对应了请求和响应。顾名思义,我们可以联想到请求入参和响应出参。没错,Spring就是根据我们请求、响应时带的MediaType来帮助我们选择合适的实现类完成我们的请求、响应的。
因为HttpMessageConverter
是跟请求和响应有关的,那自然少不了RequestMappingHandlerAdapter
了呀。因为这个类是实际执行mvc请求的handler方法的地方。既然是执行方法,肯定有对方法入参的解析以及对响应数据的处理。
进入到RequestMappingHandlerAdapter
类,发现这个无参的构造函数有点意思,拿出来看下:
public RequestMappingHandlerAdapter() {
this.messageConverters = new ArrayList<>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
//将AllEncompassingFormHttpMessageConverter方法标注在下面了
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
static {
ClassLoader classLoader = AllEncompassingFormHttpMessageConverter.class.getClassLoader();
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
}
public AllEncompassingFormHttpMessageConverter() {
try {
addPartConverter(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
if (jaxb2Present && !jackson2XmlPresent) {
addPartConverter(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
addPartConverter(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
addPartConverter(new JsonbHttpMessageConverter());
}
if (jackson2XmlPresent) {
addPartConverter(new MappingJackson2XmlHttpMessageConverter());
}
if (jackson2SmilePresent) {
addPartConverter(new MappingJackson2SmileHttpMessageConverter());
}
}
这不正是初始化HttpMessageConverter
实现类的地方吗?尤其是下面的方法,根据classpath下是否存在该类,而选择加载对应的HttpMessageConverter
。
我们点开一个实现类从类描述信息发现,其实不同的HttpMessageConverter
实现类 是针对不同的MediaType的。
比如MappingJackson2HttpMessageConverter
,默认情况下,该转换器支持包含UTF-8
字符集的application/ json
和application/*+json
媒体类型。
那我们就拿我们最常用的基于application/json
的MappingJackson2HttpMessageConverter
来分析下:
public MappingJackson2HttpMessageConverter() {
//无参的构造函数调用有参的构造函数
this(Jackson2ObjectMapperBuilder.json().build());
}
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
//将applicat/json 这个媒体类型传递给父类AbstractJackson2HttpMessageConverter中supportedMediaTypes
super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}
AbstractJackson2HttpMessageConverter
类
先分析下read
@Override
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
return canRead(clazz, null, mediaType);
}
@Override
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
//这里调用AbstractHttpMessageConverter#canRead
//大意是说如果mediaType为空 返回true
//否则拿supportedMediaTypes与mediaType比较,
//如果supportedMediaTypes包含了mediaType则返回true
if (!canRead(mediaType)) {
return false;
}
//为指定类型和上下文返回Jackson
//个人理解 这个JavaType就包含了待入参处理参数的类型
JavaType javaType = getJavaType(type, contextClass);
AtomicReference<Throwable> causeRef = new AtomicReference<>();
//如果objectMapper可以反序列化javaType 那么就可读
if (this.objectMapper.canDeserialize(javaType, causeRef)) {
return true;
}
logWarningIfNecessary(javaType, causeRef.get());
return false;
}
@Override
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(type, contextClass);
return readJavaType(javaType, inputMessage);
}
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(clazz, null);
return readJavaType(javaType, inputMessage);
}
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
try {
//如果请求的HttpInputMessage报文属于MappingJacksonInputMessage这个类
if (inputMessage instanceof MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
//最终这里 就和我们熟知的json反序列化一致了。
//至此 application/json 入参转为对象完成
return this.objectMapper.readerWithView(deserializationView).forType(javaType).
readValue(inputMessage.getBody());
}
}
return this.objectMapper.readValue(inputMessage.getBody(), javaType);
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
}
}
有个read相关操作的铺垫,那write操作应该就更好分析了,这里就简单地看下write方法吧。
@Override
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
MediaType contentType = outputMessage.getHeaders().getContentType();
JsonEncoding encoding = getJsonEncoding(contentType);
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
writePrefix(generator, object);
Object value = object;
Class<?> serializationView = null;
FilterProvider filters = null;
JavaType javaType = null;
if (object instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
filters = container.getFilters();
}
if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
javaType = getJavaType(type, null);
}
//这里就是将响应对象序列化的地方,简单的讲 跟我们平时json序列化一样
ObjectWriter objectWriter = (serializationView != null ?
this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
if (filters != null) {
objectWriter = objectWriter.with(filters);
}
if (javaType != null && javaType.isContainerType()) {
objectWriter = objectWriter.forType(javaType);
}
SerializationConfig config = objectWriter.getConfig();
if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
objectWriter = objectWriter.with(this.ssePrettyPrinter);
}
objectWriter.writeValue(generator, value);
writeSuffix(generator, object);
generator.flush();
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
}
}
从我的角度出发,HttpMessageConverter
在框架里的调用是跟content_type
息息相关的。而分析HttpMessageConverter
中方法的运行流程,也大同小异:先判断是否可读/可写,如果可以,在读/写成content_type
希望的格式。
下面列出了常用HttpMessageConverter
与媒体类型的对应信息:
HttpMessageConverter | 处理哪些格式的数据 |
---|---|
StringHttpMessageConverter | 读取字符串格式的数据和写出二进制格式的数据 |
ByteArrayHttpMessageConverter | 读取二进制格式的数据和写出二进制格式的数据 |
FormHttpMessageConverter | 负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据 |
ResourceHttpMessageConverter | 负责读取资源文件和写出资源文件数据 |
Jaxb2RootElementHttpMessageConverter | 读取和写入xml 标签格式的数据 |
ResourceHttpMessageConverter | 负责读取资源文件和写出资源文件数据 |
MappingJacksonHttpMessageConverter | 读取和写入json格式的数据 |
至于HttpMessageConverter
在Spring框架中是如何使用的?会在分析HandlerMethodArgumentResolver时一并分析的。因为在解析@RequstBody方法参数的解析少不了HttpMessageConverter
的参与。