Spring MVC 源码分析 - HandlerAdapter 组件(五)之 HttpMessageConverter
参考 知识星球 中 芋道源码 星球的源码解析,一个活跃度非常高的 Java 技术社群,感兴趣的小伙伴可以加入 芋道源码 星球,一起学习😄
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读
Spring 版本:5.1.14.RELEASE
该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》
HandlerAdapter 组件
HandlerAdapter 组件,处理器的适配器。因为处理器 handler
的类型是 Object 类型,需要有一个调用者来实现 handler
是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping
注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器
由于 HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,所以将这部分内容拆分成了五个模块,依次进行分析:
- 《HandlerAdapter 组件(一)之 HandlerAdapter》
- 《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》
- 《HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver》
- 《HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler》
- 《HandlerAdapter 组件(五)之 HttpMessageConverter》
HandlerAdapter 组件(五)之 HttpMessageConverter
本文是接着《HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler》一文来分享 HttpMessageConverter 组件。在 HandlerAdapter
执行处理器的过程中,具体的执行过程交由 ServletInvocableHandlerMethod
对象来完成,其中需要先通过 HandlerMethodArgumentResolver
参数解析器从请求中解析出方法的入参,然后再通过反射机制调用对应的方法,获取到执行结果后需要通过 HandlerMethodReturnValueHandler
结果处理器来进行处理。在处理返回结果的过程中,可能需要通过 HttpMessageConverter 消息转换器将返回结果设置到响应体中,当然也可能需要通过它从请求体获取入参。
在使用 Spring MVC 时,@RequestBody
和 @ResponseBody
两个注解,分别完成请求报文到 Java 对象、Java 对象到响应报文的转换,底层的实现就是通过 Spring 3.x 中引入的 HttpMessageConverter 消息转换机制来实现的。
再开始阅读本文之前,先来理解一些概念。在处理 HTTP 请求的过程中,需要解析请求体,返回结果设置到响应体。在 Servlet 标准中,javax.servlet.ServletRequest
和 javax.servlet.ServletResponse
分别有有以下方法:
// javax.servlet.ServletRequest
public ServletInputStream getInputStream() throws IOException;
// javax.servlet.ServletResponse
public ServletOutputStream getOutputStream() throws IOException;
通过上面两个方法可以获取到请求体和响应体,ServletInputStream 和 ServletOutputStream 分别继承 java 中的 InputStream 和 OutputStream 流对象,可以通过它们获取请求报文和设置响应报文。我们只能从流中读取原始的字符串报文,或者往流中写入原始的字符串,而 Java 是面向对象编程的,字符串与 Java 对象之间的转换不可能交由开发者去实现。在 Sping MVC 中,会将 Servlet 提供的请求和响应进行一层抽象封装,便于操作读取和写入,再通过 HttpMessageConverter 消息转换机制来解析请求报文或者设置响应报文。
回顾
先来回顾一下 HandlerMethodReturnValueHandler
如何处理放回结果的,可以回到 《HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler》 中 RequestResponseBodyMethodProcessor 小节下面的 handleReturnValue
方法和 writeWithMessageConverters
方法
handleReturnValue
// RequestResponseBodyMethodProcessor.java
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// <1> 设置已处理
mavContainer.setRequestHandled(true);
// <2> 创建请求和响应
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// <3> 使用 HttpMessageConverter 对对象进行转换,并写入到响应
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
// AbstractMessageConverterMethodProcessor.java
protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
return new ServletServerHttpRequest(servletRequest);
}
// AbstractMessageConverterMethodProcessor.java
protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) {
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Assert.state(response != null, "No HttpServletResponse");
return new ServletServerHttpResponse(response);
}
上面会将请求封装成 ServletServerHttpRequest 和 ServletServerHttpResponse 对象
- ServletServerHttpRequest:实现了 ServerHttpRequest、HttpRequest、HttpInputMessage、HttpMessage接口
- ServletServerHttpResponse:实现 ServerHttpResponse、HttpOutputMessage 接口
上面这些接口定义了一些获取请求和设置响应相关信息的方法,便于获取请求和设置响应
writeWithMessageConverters
// AbstractMessageConverterMethodProcessor.java
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// <1> 获得 body、valueType、targetType
Object body; Class<?> valueType; Type targetType;
// <3> 选择使用的 MediaType
MediaType selectedMediaType = null;
// <4> 如果匹配到,则进行写入逻辑
if (selectedMediaType != null) {
// <4.1> 移除 quality 。例如,application/json;q=0.8 移除后为 application/json
selectedMediaType = selectedMediaType.removeQualityValue();
// <4.2> 遍历 messageConverters 数组
for (HttpMessageConverter<?> converter : this.messageConverters) {
// <4.3> 判断 HttpMessageConverter 是否支持转换目标类型
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter
? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType)
: converter.canWrite(valueType, selectedMediaType)) {
// <5.2> body 非空,则进行写入
if (body != null) {
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
} else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
// <5.4> return 返回,结束整个逻辑
return;
}
}
}
// ... 上面省略了大量代码
}
<4.2>
处,遍历所有的 HttpMessageConverter 实现类
<4.3>
处,调用当前 HttpMessageConverter 实现类的 canWrite
方法,判断是否支持写入
<5.2>
处,调用该 HttpMessageConverter 实现类的 write
方法,进行写入
HttpInputMessage 接口
org.springframework.http.HttpInputMessage
:对一次 Http 请求报文的抽象
public interface HttpInputMessage extends HttpMessage {
/**
* Return the body of the message as an input stream.
* @return the input stream body (never {@code null})
* @throws IOException in case of I/O errors
*/
InputStream getBody() throws IOException;
}
在 HttpMessageConverter 的 read
方法中,有一个 HttpInputMessage 的形参,它正是 Spring MVC 的消息转换器所作用的受体请求消息的内部抽象,消息转换器从请求消息中按照规则提取消息,转换为方法形参中声明的对象。
HttpOutputMessage 接口
org.springframework.http.HttpOutputMessage
:对一次 Http 响应报文的抽象
public interface HttpOutputMessage extends HttpMessage {
/**
* Return the body of the message as an output stream.
* @return the output stream body (never {@code null})
* @throws IOException in case of I/O errors
*/
OutputStream getBody() throws IOException;
}
在 HttpMessageConverter 的 write
方法中,有一个 HttpOutputMessage 的形参,它正是 Spring MVC 的消息转换器所作用的受体响应消息的内部抽象,消息转换器将响应消息按照一定的规则写到响应报文中
HttpMessageConverter 接口
org.springframework.http.converter.HttpMessageConverter
:对消息转换器最高层次的接口抽象,描述了一个消息转换器的一般特征
public interface HttpMessageConverter<T> {
/** 能否读取 */
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
/** 能够写入 */
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
/** 获取支持的 MediaType */
List<MediaType> getSupportedMediaTypes();
/** 读取请求体 */
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
/** 设置响应体 */
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
类图
HttpMessageConverter 接口体系的结构如下:
上图只列出了部分实现类,因为在 Spring MVC 和 Sping Boot 中默认的 HttpMessageConverter 实现类差不多就上面几个,我们来看看有哪些实现类:
-
Spring MVC
-
Spring Boot
示例
@RestController
public class UserController {
@Autowired
UserService userService;
/** 这里的 @RequestBody 标注入参仅示例,是为了后续的分析 */
@GetMapping(value = "/query")
public Result<List<User>> queryUser(@RequestBody String name) {
try {
return Result.success().data(userService.queryUserByName(name));
} catch (Exception e) {
return Result.fail(e);
}
}
}
当你发起一个 HTTP 请求 GET /query
,因为你添加了@RequestBody 注解,所以是从请求体读请求报文的,可以设置请求体中的数据为 ming
,也就是我想要拿到名字为 ming
的所有用户的信息
Spring MVC 处理该请求时,会先进入到 RequestResponseBodyMethodProcessor 这个类,获取方法入参,其中会通过 StringHttpMessageConverter
从请求体中读取 ming
数据,作为调用方法的入参。
在 Spring MVC 获取到方法的返回结果后,又会进入到 RequestResponseBodyMethodProcessor 这个类,往响应体中写数据,其中会通过 MappingJackson2HttpMessageConverter
将 List<User>
返回结果写入响应。
提示:RequestResponseBodyMethodProcessor 既是参数解析器,也是返回结果处理器
总结下来,整个过程如下所示:
AbstractHttpMessageConverter
org.springframework.http.converter.AbstractHttpMessageConverter
,实现 HttpMessageConverter 接口,提供通用的骨架方法
构造方法
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
/**
* 支持的 MediaType
*/
private List<MediaType> supportedMediaTypes = Collections.emptyList();
/**
* 默认的字符集
*/
@Nullable
private Charset defaultCharset;
protected AbstractHttpMessageConverter() {
}
protected AbstractHttpMessageConverter(MediaType supportedMediaType) {
setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
}
protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) {
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
}
protected AbstractHttpMessageConverter(Charset defaultCharset, MediaType... supportedMediaTypes) {
this.defaultCharset = defaultCharset;
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
}
}
-
supportedMediaTypes
:支持的 MediaType -
defaultCharset
:默认的字符集
上面两个属性可以由子类去设置
getSupportedMediaTypes
实现 getSupportedMediaTypes()
方法,获得支持的 MediaType,如下:
@Override
public List<MediaType> getSupportedMediaTypes() {
return Collections.unmodifiableList(this.supportedMediaTypes);
}
canRead
实现 canRead(Class<?> clazz, @Nullable MediaType mediaType)
方法,是否支持从请求中读取该类型的方法参数,如下:
@Override
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canRead(mediaType);
}
protected abstract boolean supports(Class<?> clazz);
protected boolean canRead(@Nullable MediaType mediaType) {
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
其中 supports(Class<?> clazz)
抽象方法,交由子类去实现
canWrite
实现 canWrite(Class<?> clazz, @Nullable MediaType mediaType)
方法,是否支持往响应中写入该类型的返回结果,如下:
@Override
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canWrite(mediaType);
}
protected abstract boolean supports(Class<?> clazz);
protected boolean canWrite(@Nullable MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.isCompatibleWith(mediaType)) {
return true;
}
}
return false;
}
其中 supports(Class<?> clazz)
抽象方法,交由子类去实现
read
实现 read(Class<? extends T> clazz, HttpInputMessage inputMessage)
方法,从请求中读取该类型的方法参数,如下:
@Override
public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return readInternal(clazz, inputMessage);
}
protected abstract T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
其中 readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)
抽象方法,交由子类去实现
write
实现 write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
方法,往响应中写入该类型的返回结果,如下:
@Override
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// <1> 获取响应头
final HttpHeaders headers = outputMessage.getHeaders();
// <2> 如果 Content-Type 为空则设置默认的
addDefaultHeaders(headers, t, contentType);
// <3> 往响应中写入数据
if (outputMessage instanceof StreamingHttpOutputMessage) { // <3.1> 如果是流,则再封装一层
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() {
@Override
public OutputStream getBody() {
return outputStream;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}));
}
else { // <3.2> 普通对象
writeInternal(t, outputMessage);
outputMessage.getBody().flush();
}
}
protected abstract void writeInternal(T t, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
-
获取响应头
-
如果
Content-Type
为空则设置默认的 -
往响应中写入数据
- 如果是流,则再封装一层,StreamingHttpOutputMessage 对象
- 普通对象,则直接调用
writeInternal(T t, HttpOutputMessage outputMessage)
抽象方法
-
刷出流
StringHttpMessageConverter
org.springframework.http.converter.StringHttpMessageConverter
,继承 AbstractHttpMessageConverter 抽象类,String 类型的消息转换器
supports
实现 supports(Class<?> clazz)
方法,是否支持从请求中读取该类型的方法参数,或者是否支持往响应中写入该类型的返回结果,如下:
@Override
public boolean supports(Class<?> clazz) {
return String.class == clazz;
}
是 String
类就可以,所以在示例中,会使用 StringHttpMessageConverter
消息转换器
readInternal
实现 readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage)
方法,从请求中读取该类型的方法参数,如下:
@Override
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
return StreamUtils.copyToString(inputMessage.getBody(), charset);
}
// org.springframework.util.StreamUtils.java
public static String copyToString(@Nullable InputStream in, Charset charset) throws IOException {
if (in == null) {
return "";
}
StringBuilder out = new StringBuilder();
InputStreamReader reader = new InputStreamReader(in, charset);
char[] buffer = new char[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = reader.read(buffer)) != -1) {
out.append(buffer, 0, bytesRead);
}
return out.toString();
}
逻辑不复杂,直接从请求的 ServletInputStream 流中读取出来,转换成字符串
AbstractJackson2HttpMessageConverter
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter
:继承 AbstractGenericHttpMessageConverter 抽象类,JSON 格式的消息读取或者写入,也就是我们熟悉的 @RequestBody
和 @ResponseBody
注解对应的 HttpMessageConverter 消息转换器
构造方法
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
/**
* The default charset used by the converter.
*/
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
protected ObjectMapper objectMapper;
@Nullable
private Boolean prettyPrint;
@Nullable
private PrettyPrinter ssePrettyPrinter;
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
setDefaultCharset(DEFAULT_CHARSET);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
this.ssePrettyPrinter = prettyPrinter;
}
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType supportedMediaType) {
this(objectMapper);
setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
}
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes) {
this(objectMapper);
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
}
}
canRead
实现 canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType)
方法,是否支持从请求中读取该类型的方法参数,如下:
@Override
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
if (!canRead(mediaType)) {
return false;
}
// 获得方法入参的类型
JavaType javaType = getJavaType(type, contextClass);
AtomicReference<Throwable> causeRef = new AtomicReference<>();
// 通过 ObjectMapper 判断是否能够反序列化
if (this.objectMapper.canDeserialize(javaType, causeRef)) {
return true;
}
logWarningIfNecessary(javaType, causeRef.get());
return false;
}
read
实现 read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
方法,从请求中读取该类型的方法参数,如下:
@Override
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
// 获得方法入参的类型
JavaType javaType = getJavaType(type, contextClass);
// 从请求中读取该类型的方法入参
return readJavaType(javaType, inputMessage);
}
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
try {
// 如果请求是 MappingJacksonInputMessage 类型,默认不是
if (inputMessage instanceof MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
return this.objectMapper.readerWithView(deserializationView).forType(javaType).
readValue(inputMessage.getBody());
}
}
// 通过 ObjectMapper 从请求中读取该类型的方法入参
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);
}
}
canWrite
实现 canWrite(Class<?> clazz, @Nullable MediaType mediaType)
方法,判断是否支持往响应中写入该类型的返回结果,如下:
@Override
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
// 判断是否支持该 MediaType,也就是 Content-Type
if (!canWrite(mediaType)) {
return false;
}
AtomicReference<Throwable> causeRef = new AtomicReference<>();
// 通过 ObjectMapper 判断是否能够序列化
if (this.objectMapper.canSerialize(clazz, causeRef)) {
return true;
}
logWarningIfNecessary(clazz, causeRef.get());
return false;
}
- 判断是否支持该 MediaType,也就是 Content-Type,支持
application/json
、application/*+json
- 通过 ObjectMapper 判断是否能够序列化
writeInternal
实现 writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
方法,往响应中写入该类型的返回结果,如下:
@Override
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// <1> 获取编码方式
// <1.1> 获取 Content-Type,例如 `application/json;charset=UTF-8`
MediaType contentType = outputMessage.getHeaders().getContentType();
// <1.2> 从 Content-Type 获取编码方式,默认 UTF8
JsonEncoding encoding = getJsonEncoding(contentType);
// <2> 构建一个 Json 生成器 `generator`,指定`输出流(响应)`和编码
// 例如:UTF8JsonGenerator 对象(jackson-core 包)
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
// <3> 设置前缀,默认没有
writePrefix(generator, object);
// <4> 获得方法的返回结果对象 `value`,返回结果类型 `javaType`
Object value = object;
Class<?> serializationView = null;
FilterProvider filters = null;
JavaType javaType = null;
// <4.1> 如果返回结果对象是 MappingJacksonValue 类型,没使用过
if (object instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
filters = container.getFilters();
}
// <4.2> 获取方法的返回结果的类型 `javaType`
if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
javaType = getJavaType(type, null);
}
// <5> 创建 ObjectWriter 对象 `objectWriter`,没有特殊配置通过 `this.objectMapper.writer()` 生成
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);
}
// <6> 获取序列化配置
SerializationConfig config = objectWriter.getConfig();
if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
objectWriter = objectWriter.with(this.ssePrettyPrinter);
}
// <7> **【重点】**通过 `objectWriter` 将返回结果进行序列化,设置到 `generator` 中
objectWriter.writeValue(generator, value);
// <8> 设置后缀,默认没有
writeSuffix(generator, object);
// <9> 让 `generator` 刷出数据,以 Json 格式输出,也就是会往响应中刷出 Json 格式的返回结果
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);
}
}
-
获取编码方式
- 获取 Content-Type,例如
application/json;charset=UTF-8
- 从 Content-Type 获取编码方式,默认 UTF8
- 获取 Content-Type,例如
-
构建一个 Json 生成器
generator
,指定输出流(响应)
和编码 -
调用
writePrefix(JsonGenerator generator, Object object)
方法,设置前缀,MappingJackson2HttpMessageConverter 默认没有 -
获得方法的返回结果对象
value
,返回结果类型javaType
- 如果返回结果对象是 MappingJacksonValue 类型,则从该对象中相关属性中获取,没使用过😈
- 获取方法的返回结果的类型
javaType
-
创建 ObjectWriter 对象
objectWriter
,没有特殊配置通过this.objectMapper.writer()
生成 -
获取序列化配置
-
【重点】通过
objectWriter
将返回结果进行序列化,设置到generator
中 -
调用
writeSuffix(JsonGenerator generator, Object object)
方法,设置后缀,MappingJackson2HttpMessageConverter 默认没有 -
让
generator
刷出数据,以 Json 格式输出,也就是会往响应中刷出 Json 格式的返回结果
MappingJackson2HttpMessageConverter
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
,继承 AbstractJackson2HttpMessageConverter 抽象类,JSON 格式的消息读取或者写入,也就是我们熟悉的 @RequestBody
和 @ResponseBody
注解对应的 HttpMessageConverter 消息转换器
public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
@Nullable
private String jsonPrefix;
public MappingJackson2HttpMessageConverter() {
this(Jackson2ObjectMapperBuilder.json().build());
}
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}
public void setJsonPrefix(String jsonPrefix) {
this.jsonPrefix = jsonPrefix;
}
public void setPrefixJson(boolean prefixJson) {
this.jsonPrefix = (prefixJson ? ")]}', " : null);
}
@Override
protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
if (this.jsonPrefix != null) {
generator.writeRaw(this.jsonPrefix);
}
}
}
可以看到仅是添加了一个 jsonPrefix
属性,JSON 的前缀,默认为空,但是只有前缀,没有后缀吗?没搞明白😈
思考
张小龙在谈微信的本质时候说:“微信只是个平台,消息在其中流转”。在 Spring MVC 的 HttpMessageConverter 机制中可以领悟到类似的道理,一次请求报文和一次响应报文,分别被抽象为一个请求消息 HttpInputMessage 和一个响应消息 HttpOutputMessage
处理请求时,由合适的 HttpMessageConverter 消息转换器将请求报文绑定为方法中的形参对象,同一个对象就有可能出现多种不同的消息形式,比如 json 和 xml,同样,当响应请求时,方法的返回值也同样可能被返回为不同的消息形式,比如 json 和 xml
在 Spring MVC 中,针对不同的消息形式,有不同的 HttpMessageConverter 实现类来处理各种消息形式。但是,只要这些消息所蕴含的“有效信息”是一致的,那么各种不同的消息转换器,都会生成同样的转换结果。至于各种消息间解析细节的不同,就被屏蔽在不同的 HttpMessageConverter 实现类中了
总结
在 HandlerAdapter
执行 HandlerMethod
处理器的过程中,会将该处理器封装成 ServletInvocableHandlerMethod
对象,通过该对象来执行处理器。该对象通过反射机制调用对应的方法,在调用方法之前,借助 HandlerMethodArgumentResolver
参数解析器从请求中获取到对应的方法参数值,在调用方法之后,需要借助于HandlerMethodReturnValueHandler
返回值处理器将返回结果设置到响应中,或者设置相应的 Model 和 View 用于后续的视图渲染。在这整个过程中需要 HttpMessageConverter 消息转换器从请求中获取方法入参,或者往响应中设置返回结果。
HttpMessageConverter 的实现类非常多,本文分析了我们常用的两种方法出入参格式,标注 @RequestBody
注解方法参数和标注 @ResponseBody
注解的方法
StringHttpMessageConverter
:处理 String 类型的方法入参,直接从请求体中读取,转换成字符串,当然也可以往响应中写入 String 类型的返回结果MappingJackson2HttpMessageConverter
:处理标有@RequestBody
注解的方法参数或者返回结果,解析或者输出 JSON 格式的数据,需要通过ObjectMapper
和ObjectWriter
进行反序列化和序列化等操作,也需要通过JsonGenerator
进行 JSON 格式的消息输出
Spring MVC 默认的 JSON 消息格式的转换器是 MappingJackson2HttpMessageConverter
这个类,不过他仅定义了一个 JSON 前缀属性,主要的实现在其父类 AbstractJackson2HttpMessageConverter
完成的
本文对 Spring MVC 中的 HttpMessageConverter 仅做了一个浅显的分析,对消息转换机制有个认识就好了。至此,关于 Spring MVC 中 HandlerAdapter
组件涉及到的 HandlerAdapter
、ServletInvocableHandlerMethod
、HandlerMethodArgumentResolver
、HandlerMethodReturnValueHandler
、HttpMessageConverter
五个组件都分析完了
HandlerAdapter 真的是 Spring MVC 九大组件里,最复杂的一个😜
参考文章:芋道源码《精尽 Spring MVC 源码分析》