Spring MVC HttpMessageConverter 的作用是什么?
HttpMessageConverter
(HTTP 消息转换器) 是 Spring MVC 框架中一个非常核心的组件,它的主要作用是在 HTTP 请求、响应体与 Java 对象之间进行双向转换。
核心作用:
-
读取请求体 (Request Body) 到 Java 对象:
- 当 Controller 方法的参数使用
@RequestBody
注解时,Spring MVC 会尝试找到一个合适的HttpMessageConverter
来读取 HTTP 请求体的内容 (例如,JSON 或 XML 字符串),并将其反序列化 (deserialize) 成方法参数指定的 Java 对象类型。 - 例如,一个 POST 请求发送了 JSON 数据
{ "name": "John", "age": 30 }
,如果 Controller 方法参数是@RequestBody User user
,那么某个HttpMessageConverter
(如MappingJackson2HttpMessageConverter
) 会负责将这个 JSON 转换为一个User
对象。
- 当 Controller 方法的参数使用
-
将 Java 对象写入响应体 (Response Body):
- 当 Controller 方法使用
@ResponseBody
注解 (或者 Controller 类使用@RestController
注解) 标记时,Spring MVC 会尝试找到一个合适的HttpMessageConverter
来将方法的返回值 (通常是一个 Java 对象) 序列化 (serialize) 成特定格式 (例如,JSON 或 XML 字符串),并将其写入 HTTP 响应体。 - 例如,一个 Controller 方法返回一个
User
对象,如果内容协商确定应该返回 JSON,那么某个HttpMessageConverter
会将User
对象转换为 JSON 字符串并发送给客户端。
- 当 Controller 方法使用
它是如何实现自动转换的 (特别是 JSON/XML)?
这个自动转换过程涉及到以下几个关键步骤和组件:
-
注册
HttpMessageConverter
实例:- Spring MVC (在 Spring Boot 环境下,通常是自动) 会在应用启动时初始化并注册一个
HttpMessageConverter
列表。 - 这些转换器是针对不同数据格式的,例如:
MappingJackson2HttpMessageConverter
: 用于 JSON 格式的转换,它底层使用 Jackson 库。Jaxb2RootElementHttpMessageConverter
: 用于 XML 格式的转换,它底层使用 JAXB (Java Architecture for XML Binding)。MappingJackson2XmlHttpMessageConverter
: 也是用于 XML,但使用 Jackson 的 XML 模块。StringHttpMessageConverter
: 用于纯文本字符串。ByteArrayHttpMessageConverter
: 用于字节数组。- 还有其他用于表单数据、Protobuf 等的转换器。
- Spring Boot 会根据类路径上的依赖自动配置这些转换器。例如,如果
jackson-databind
在类路径上,MappingJackson2HttpMessageConverter
就会被自动注册。
- Spring MVC (在 Spring Boot 环境下,通常是自动) 会在应用启动时初始化并注册一个
-
内容协商 (Content Negotiation) 确定目标媒体类型:
- 对于响应 (对象 -> 响应体):
- Spring MVC 通过
ContentNegotiationManager
来确定应该返回给客户端的媒体类型 (MIME type)。 - 它会考虑:
- Controller 方法上
@RequestMapping
(或其变体) 的produces
属性。 - 客户端请求的
Accept
HTTP 头。 - URL 路径扩展名 (如
.json
,.xml
) 或 URL 参数 (如?format=json
) (如果配置了这些策略)。
- Controller 方法上
- 一旦确定了目标媒体类型 (例如
application/json
)。
- Spring MVC 通过
- 对于请求 (请求体 -> 对象):
- Spring MVC 会查看请求的
Content-Type
HTTP 头,以了解请求体中的数据是什么格式 (例如application/json
)。
- Spring MVC 会查看请求的
- 对于响应 (对象 -> 响应体):
-
选择合适的
HttpMessageConverter
:- 对于响应:
- Spring MVC 会遍历已注册的
HttpMessageConverter
列表。 - 对于每个转换器,它会调用其
canWrite(Class<?> clazz, MediaType mediaType)
方法。 canWrite
方法会检查该转换器是否能够将给定的 Java 对象类型 (clazz
,即 Controller 方法的返回值类型) 序列化为之前内容协商确定的目标媒体类型 (mediaType
)。- 找到第一个返回
true
的转换器,就用它来进行转换。
- Spring MVC 会遍历已注册的
- 对于请求:
- Spring MVC 同样会遍历已注册的
HttpMessageConverter
列表。 - 对于每个转换器,它会调用其
canRead(Class<?> clazz, MediaType mediaType)
方法。 canRead
方法会检查该转换器是否能够将请求的Content-Type
(mediaType
) 反序列化为 Controller 方法参数的目标 Java 对象类型 (clazz
)。- 找到第一个返回
true
的转换器,就用它来进行转换。
- Spring MVC 同样会遍历已注册的
- 对于响应:
-
执行转换 (序列化/反序列化):
- 对于响应 (使用选定的转换器的
write
方法):- 调用选定转换器的
write(T t, MediaType contentType, HttpOutputMessage outputMessage)
方法。 t
是 Controller 返回的 Java 对象。contentType
是协商好的响应媒体类型。outputMessage
提供了写入 HTTP 响应体的方式。- JSON 示例 (使用
MappingJackson2HttpMessageConverter
):- Jackson 的
ObjectMapper
会被用来将 Java 对象序列化为 JSON 字符串。 - 这个 JSON 字符串会被写入到
HttpOutputMessage
的输出流中。
- Jackson 的
- XML 示例 (使用
Jaxb2RootElementHttpMessageConverter
):- JAXB 的
Marshaller
会被用来将带有 JAXB 注解的 Java 对象序列化为 XML 字符串。 - 这个 XML 字符串会被写入到输出流中。
- JAXB 的
- 调用选定转换器的
- 对于请求 (使用选定的转换器的
read
方法):- 调用选定转换器的
read(Class<? extends T> clazz, HttpInputMessage inputMessage)
方法。 clazz
是 Controller 方法参数的目标 Java 对象类型。inputMessage
提供了读取 HTTP 请求体的方式。- JSON 示例 (使用
MappingJackson2HttpMessageConverter
):- Jackson 的
ObjectMapper
会从HttpInputMessage
的输入流中读取 JSON 数据。 - 然后将 JSON 数据反序列化为指定
clazz
类型的 Java 对象。
- Jackson 的
- XML 示例 (使用
Jaxb2RootElementHttpMessageConverter
):- JAXB 的
Unmarshaller
会从输入流中读取 XML 数据。 - 然后将 XML 数据反序列化为指定
clazz
类型的 (带有 JAXB 注解的) Java 对象。
- JAXB 的
- 调用选定转换器的
- 对于响应 (使用选定的转换器的
简而言之,整个过程可以概括为:
- 配置阶段: 注册一系列支持不同媒体类型的
HttpMessageConverter
。 - 请求处理阶段 (读取请求体):
- 获取请求的
Content-Type
。 - 根据
Content-Type
和目标对象类型,选择一个HttpMessageConverter
。 - 使用该转换器将请求体内容(如JSON/XML)反序列化为Java对象。
- 获取请求的
- 响应处理阶段 (写入响应体):
- 通过内容协商确定响应的
Content-Type
。 - 根据返回对象类型和协商好的
Content-Type
,选择一个HttpMessageConverter
。 - 使用该转换器将Java对象序列化为响应体内容(如JSON/XML)。
- 通过内容协商确定响应的
开发中如果我们需要支持新的数据格式,只需要实现并注册一个新的 HttpMessageConverter
即可。