初识扩展消息转换器

38 阅读3分钟

1)消息转换器是干嘛的?

你可以把一次接口调用想成:

前端说 JSON → 后端要变成 Java 对象
后端算完得到 Java 对象 → 要变成 JSON 再返回给前端

这个“互相翻译”的工作,就是 HttpMessageConverter 做的。

  • 请求进来:把请求体(Body)里的 JSON → 转成 DTO/Entity
  • 响应出去:把返回值对象 VO/Entity → 转成 JSON

所以你写的:

@PostMapping("/login")
public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO dto)

这里的 @RequestBody 能把 JSON 变 DTO,就是消息转换器在干活。


2)它什么时候会被用到?

只要你满足这两种情况,基本就会走消息转换器:

✅ 进来用(反序列化)

  • Controller 参数上有 @RequestBody
  • 或请求是 Content-Type: application/json

例子:JSON → EmployeeLoginDTO

✅ 出去用(序列化)

  • Controller 返回对象(不是 view)
  • 或类上有 @RestController

例子:Result<EmployeeLoginVO> → JSON


3)为什么要“扩展消息转换器”?你这个案例解决啥问题?

你扩展的是 MappingJackson2HttpMessageConverter,它负责:

Java对象 ⇄ JSON 的转换(用的是 Jackson)

默认 Jackson 对时间(LocalDateTime 等)序列化出来可能是:

  • 时间戳、数组形式、或带秒/时区不一致
  • 前端想要统一格式:比如 "yyyy-MM-dd HH:mm"

所以你们做了一个 自定义 ObjectMapper(JacksonObjectMapper) ,专门规定:

  • LocalDateTime"yyyy-MM-dd HH:mm"
  • LocalDate"yyyy-MM-dd"
  • LocalTime"HH:mm:ss"

这样你后端所有返回时间字段都会“长得一致”,前端也不用各种兼容。


4)为什么要 converters.add(0, converter) 放到最前面?

Spring 会维护一堆消息转换器(你看到默认 8 个)。

当它要写出响应时,会按顺序找:

支持 当前返回类型 + 当前响应媒体类型(比如 application/json)
找到第一个能用的就用它

如果你不放在最前面,可能会被默认的 Jackson converter 抢先用掉,你的时间格式配置就不生效或不稳定。

放到索引 0 的意思就是:

我这个翻译官优先级最高,先用我的规则翻译


5)extendMessageConverters 和 configureMessageConverters 区别(很关键)

  • extendMessageConverters(...)在默认转换器基础上加/改(推荐,安全)
  • configureMessageConverters(...)你全接管,默认的都没了(容易出坑)

你们用的是 extend,属于“加一条自己的规则”,这就是推荐做法。


6)你打断点看到的“8个转换器”大概是啥?

不用背,只要知道它们是“不同语言的翻译官”,比如:

  • String 翻译官:处理纯文本
  • ByteArray 翻译官:处理二进制
  • Form 表单翻译官:处理 application/x-www-form-urlencoded
  • Jackson 翻译官:处理 JSON(你现在是增强它)

7)你该怎么验证它生效?

最简单的验证方式:

  1. 找一个接口返回对象里有 LocalDateTime 字段(比如创建时间)

  2. 调接口看响应 JSON:

    • 是否是 "2025-12-28 10:30" 这种格式(按你配置的 "yyyy-MM-dd HH:mm"

如果没变,通常是:

  • converter 没放到最前面
  • 你没用 JacksonObjectMapper(或者被别的 ObjectMapper 覆盖)
  • 返回的时间字段不是 LocalDateTime(比如是 Date 或 String)