Spring MVC 源码- HandlerAdapter 组件(五)之 HttpMessageConverter
创始人
2024-05-26 20:49:15

HandlerAdapter 组件

HandlerAdapter 组件,处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器

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.javaprotected  void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// <1> 获得 body、valueType、targetTypeObject body; Class valueType; Type targetType;// <3> 选择使用的 MediaTypeMediaType selectedMediaType = null;// <4> 如果匹配到,则进行写入逻辑if (selectedMediaType != null) {// <4.1> 移除 quality 。例如,application/json;q=0.8 移除后为 application/jsonselectedMediaType = 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 {/** 能否读取 */boolean canRead(Class clazz, @Nullable MediaType mediaType);/** 能够写入 */boolean canWrite(Class clazz, @Nullable MediaType mediaType);/** 获取支持的 MediaType */List getSupportedMediaTypes();/** 读取请求体 */T read(Class 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 {@AutowiredUserService userService;/** 这里的 @RequestBody 标注入参仅示例,是为了后续的分析 */@GetMapping(value = "/query")public Result> 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 返回结果写入响应。

提示:RequestResponseBodyMethodProcessor 既是参数解析器,也是返回结果处理器

总结下来,整个过程如下所示:

AbstractHttpMessageConverter

org.springframework.http.converter.AbstractHttpMessageConverter,实现 HttpMessageConverter 接口,提供通用的骨架方法

构造方法

public abstract class AbstractHttpMessageConverter implements HttpMessageConverter {/*** 支持的 MediaType*/private List supportedMediaTypes = Collections.emptyList();/*** 默认的字符集*/@Nullableprivate 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 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 clazz, HttpInputMessage inputMessage) 方法,从请求中读取该类型的方法参数,如下:

@Override
public final T read(Class clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {return readInternal(clazz, inputMessage);
}protected abstract T readInternal(Class clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException;

其中 readInternal(Class 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() {@Overridepublic OutputStream getBody() {return outputStream;}@Overridepublic HttpHeaders getHeaders() {return headers;}}));}else { // <3.2> 普通对象writeInternal(t, outputMessage);outputMessage.getBody().flush();}
}protected abstract void writeInternal(T t, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException;
  1. 获取响应头

  1. 如果 Content-Type 为空则设置默认的

  1. 往响应中写入数据

  1. 如果是流,则再封装一层,StreamingHttpOutputMessage 对象

  1. 普通对象,则直接调用 writeInternal(T t, HttpOutputMessage outputMessage) 抽象方法

  1. 刷出流

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 clazz, HttpInputMessage inputMessage) 方法,从请求中读取该类型的方法参数,如下:

@Override
protected String readInternal(Class 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 {/*** The default charset used by the converter.*/public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;protected ObjectMapper objectMapper;@Nullableprivate Boolean prettyPrint;@Nullableprivate 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 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-Typeif (!canWrite(mediaType)) {return false;}AtomicReference causeRef = new AtomicReference<>();// 通过 ObjectMapper 判断是否能够序列化if (this.objectMapper.canSerialize(clazz, causeRef)) {return true;}logWarningIfNecessary(clazz, causeRef.get());return false;
}
  1. 判断是否支持该 MediaType,也就是 Content-Type,支持 application/json、application/*+json

  1. 通过 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 获取编码方式,默认 UTF8JsonEncoding 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);}
}
  1. 获取编码方式

  1. 获取 Content-Type,例如 application/json;charset=UTF-8

  1. 从 Content-Type 获取编码方式,默认 UTF8

  1. 构建一个 Json 生成器 generator,指定输出流(响应)和编码

  1. 调用writePrefix(JsonGenerator generator, Object object)方法,设置前缀,MappingJackson2HttpMessageConverter 默认没有

  1. 获得方法的返回结果对象 value,返回结果类型 javaType

  1. 如果返回结果对象是 MappingJacksonValue 类型,则从该对象中相关属性中获取,没使用过

  1. 获取方法的返回结果的类型 javaType

  1. 创建 ObjectWriter 对象 objectWriter,没有特殊配置通过 this.objectMapper.writer() 生成

  1. 获取序列化配置

  1. 【重点】通过 objectWriter 将返回结果进行序列化,设置到 generator 中

  1. 调用 writeSuffix(JsonGenerator generator, Object object) 方法,设置后缀,MappingJackson2HttpMessageConverter 默认没有

  1. 让 generator 刷出数据,以 Json 格式输出,也就是会往响应中刷出 Json 格式的返回结果

MappingJackson2HttpMessageConverter

org.springframework.http.converter.json.MappingJackson2HttpMessageConverter,继承 AbstractJackson2HttpMessageConverter 抽象类,JSON 格式的消息读取或者写入,也就是我们熟悉的 @RequestBody 和 @ResponseBody 注解对应的 HttpMessageConverter 消息转换器

public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {@Nullableprivate 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);}@Overrideprotected 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 九大组件里,最复杂的一个

相关内容

热门资讯

苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...