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

相关推荐
许彰午1 小时前
47-MetaGrid元数据表格
java·低代码·架构
wang_shu_mo_ran1 小时前
Spring MVC的常用注解和用法(一)
java·spring·mvc
晴空蓝天1 小时前
Spring Boot 3.5 脚手架里的 JWT + Redis 双轨会话,双端 token 隔离我是这么设计的
java·spring boot·redis
IT枫斗者枫哥1 小时前
MyBatis 列表查询优化:一页20条数据,21次SQL改成2次
java
君顾11 小时前
本地电竞服务交易系统架构设计与实战:从同城服务撮合到订单履约
java·开发语言·电竞
小刘在重生~1 小时前
十六(3)、《集合扩充》CopyOnWriteArrayList 超详细解析(线程安全集合)
java·笔记·面试·职场和发展
逆境不可逃1 小时前
Pi Agent 学习笔记:对话怎样保存、分支与恢复
java
huaweichenai2 小时前
spring boot使用hutool实现调用外部接口
java·spring boot
泡海椒2 小时前
Java 后端最优 PDF 导出方案:jquick-pdf 项目引入与快速测试
java·开发语言·pdf