Spring 框架在处理 HTTP 请求和响应时,会使用配置的 HttpMessageConverter(消息转换器)来完成数据格式的转换。这是 Spring MVC 实现 RESTful API、处理 JSON/XML 数据以及 @RequestBody 和 @ResponseBody 注解功能的核心机制。
以下是关于 Spring 如何使用 HttpMessageConverter 的详细解析:
- 核心工作原理
HttpMessageConverter 是一个策略接口,负责在 HTTP 请求/响应字节流 与 Java 对象 之间进行双向转换。
读取请求(反序列化):当控制器方法参数带有 @RequestBody 时,Spring 会根据请求头中的 Content-Type媒体类型(如 application/json`)和参数类型,选择合适的 Converter 将请求体字节流转换为 Java 对象。
写入响应(序列化):当控制器方法带有 @ResponseBody 或返回值为对象且被 ResponseEntity 包裹时,Spring 会根据请求头中的 Accept 属性(客户端希望接收的类型)和返回值类型,选择合适的 Converter 将 Java 对象转换为字节流写入响应体。
- 匹配与选择机制
Spring 并不是随机选择一个转换器,而是遵循严格的匹配逻辑:
遍历转换器列表:Spring 容器中维护了一个 List<HttpMessageConverter<?>>。
判断可读/可写性:
对于请求:调用 canRead(Class<?> clazz, MediaType mediaType)。检查该转换器是否支持目标 Java 类型以及请求的 Content-Type。
对于响应:调用 canWrite(Class<?> clazz, MediaType mediaType)。检查该转换器是否支持返回的 Java 类型以及客户端接受的 MediaType(通过 Accept 头协商)。
执行转换:一旦找到第一个满足条件的 Converter,就会调用其 read() 或 write() 方法执行实际转换。如果找不到合适的 Converter,通常会抛出 HttpMediaTypeNotSupportedException (415) 或 HttpMessageNotWritableException (500/406)。
- 常用的内置 HttpMessageConverter
Spring Boot 默认会自动配置一组常用的转换器,主要包括:
|----------------------------------------|---------------------------------------|-----------------------------------------------------|----------------------------|
| 转换器类名 | 支持的 MediaType | 功能描述 | 依赖库 |
| MappingJackson2HttpMessageConverter | application/json, application/*+json | 将 Java 对象与 JSON 字符串互转。这是处理 JSON 最常用的转换器。 | Jackson (jackson-databind) |
| StringHttpMessageConverter | text/plain, */* | 将请求/响应体作为纯字符串处理。常用于接收原始文本或简单字符串返回。 | 无 (Spring Core) |
| FormHttpMessageConverter | application/x-www-form-urlencoded | 将表单数据转换为 MultiValueMap<String, String>。 | 无 (Spring Web) |
| ByteArrayHttpMessageConverter | application/octet-stream, */* | 处理二进制字节数组。 | 无 (Spring Core) |
| ResourceHttpMessageConverter | */* | 处理 org.springframework.core.io.Resource 对象,常用于文件下载。 | 无 (Spring Core) |
| Jaxb2RootElementHttpMessageConverter | application/xml, text/xml | 将 Java 对象与 XML 互转。 | JAXB |
注意:StringHttpMessageConverter 默认使用的字符集通常是 ISO-8859-1,但在 Spring Boot 中通常会被配置为 UTF-8 以避免中文乱码。
- 自定义与配置
在实际开发中,默认的转换器可能无法满足所有需求(例如特定的日期格式、Null 值处理、自定义协议等),可以通过以下方式配置:
A. 修改默认行为(推荐)
通过实现 WebMvcConfigurer 接口来定制现有的转换器,特别是针对 Jackson 的配置。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 1. 移除默认的 StringConverter 以解决潜在的编码问题或替换它
converters.removeIf(c -> c instanceof StringHttpMessageConverter);
// 2. 添加自定义编码的 StringConverter
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
stringConverter.setWriteAcceptCharset(false); // 避免在响应头中强制添加 charset
converters.add(stringConverter);
// 3. 自定义 Jackson 转换器(例如统一日期格式)
MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
jacksonConverter.setObjectMapper(objectMapper);
// 添加到列表前端,提高优先级
converters.add(0, jacksonConverter);
}
}
B. 完全自定义 Converter
如果需处理非标准格式(如特定硬件设备的私有协议),可以实现 HttpMessageConverter 接口并注册到 Spring 容器中。
- 常见陷阱与最佳实践
JSON 依赖缺失:如果使用 @ResponseBody 返回对象但项目中没有引入 Jackson 依赖(jackson-databind),Spring 无法找到 MappingJackson2HttpMessageConverter,会导致报错。确保 pom.xml 中包含相关依赖。
日期格式不一致:前端传来的日期字符串格式必须与后端 ObjectMapper 或字段上 @JsonFormat 定义的格式一致,否则会抛出 TypeMismatchException 或 JsonParseException。
Content-Type 匹配:
发送 JSON 时,请求头必须包含 Content-Type: application/json。
如果发送的是表单数据(application/x-www-form-urlencoded),则不能使用 @RequestBody 接收复杂对象,而应使用 @RequestParam 或 POJO 直接接收(由 FormHttpMessageConverter 或数据绑定机制处理)。
优先级问题:转换器的顺序很重要。Spring 按列表顺序遍历,第一个匹配的会被使用。如果需要自定义转换器优先生效,应将其添加到列表的前面(如 converters.add(0, myConverter))。
总结
Spring 通过 HttpMessageConverter 机制实现了 HTTP 协议数据与 Java 对象的解耦。开发者只需关注 Java 对象模型,而无需手动处理字节流的解析与生成。理解这一机制有助于解决常见的 415、406、400 错误以及中文乱码、日期格式化等问题。