Spring:HttpMessageConverter

Spring 框架在处理 HTTP 请求和响应时,会使用配置的 HttpMessageConverter(消息转换器)来完成数据格式的转换。这是 Spring MVC 实现 RESTful API、处理 JSON/XML 数据以及 @RequestBody 和 @ResponseBody 注解功能的核心机制。

以下是关于 Spring 如何使用 HttpMessageConverter 的详细解析:

  1. 核心工作原理

HttpMessageConverter 是一个策略接口,负责在 ‌HTTP 请求/响应字节流‌ 与 ‌Java 对象‌ 之间进行双向转换。

读取请求(反序列化)‌:当控制器方法参数带有 @RequestBody 时,Spring 会根据请求头中的 Content-Type媒体类型(如 application/json`)和参数类型,选择合适的 Converter 将请求体字节流转换为 Java 对象。

写入响应(序列化)‌:当控制器方法带有 @ResponseBody 或返回值为对象且被 ResponseEntity 包裹时,Spring 会根据请求头中的 Accept 属性(客户端希望接收的类型)和返回值类型,选择合适的 Converter 将 Java 对象转换为字节流写入响应体。

  1. 匹配与选择机制

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)。

  1. 常用的内置 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 以避免中文乱码。

  1. 自定义与配置

在实际开发中,默认的转换器可能无法满足所有需求(例如特定的日期格式、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 容器中。

  1. 常见陷阱与最佳实践

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 错误以及中文乱码、日期格式化等问题。

相关推荐
daidaidaiyu3 小时前
ThingsBoard 集群的核心逻辑源码分析
java
我可能是个假开发4 小时前
FTP Unicode 文件名导致 `550` 的问题分析与解决方案
java·开发语言·spring
caoerzhong4 小时前
中小企业上 WMS 该先上哪几块:JeeWMS 开源 Java 仓库管理系统的分批上线清单
java·python·开源
【JAVA】玩家5 小时前
Spring核心原理全解析:从零到生产实战
java·后端·spring
Wang's Blog5 小时前
Java框架 SpringCloud 快速入门: 实现 Feign 最佳实践(抽取方式)
java·spring cloud
MandalaO_O6 小时前
IDEA 开发(快捷键 + 调试 + 序列化)
java·ide·intellij-idea
xiaoqiMikko7 小时前
JVM 线上排查实战(七):jps 看不见它、jstack 连不上它,可它明明活得好好的
java·jvm
狼爷8 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言
念何架构之路8 小时前
zap扩展生态与总结
java·前端·数据库
第七页独白8 小时前
汽车零件厂如何通过 QMS 真正落地 IATF 16949——QMS软件系统:品质检验-内审稽核-8d客诉管理:全星质量管理软件系统
java·前端·数据库