通过哪些条件确定用哪个消息转换器

为了彻底搞清,我们把 Spring Boot 拆分成 "服务端(Server)""客户端(Client)" 两个角色来看。


情况一:Spring Boot 作为服务端 (最常见的 Controller 场景)

这是你写 @RestController 接收浏览器请求的时候。

  1. 处理请求 (Request) ------ 接收

    • 实际动作 :是 JSON/XML 转 Java 对象
    • 原理 :前端发来 {"name":"A"} (JSON字符串) -> Converter (read 方法) -> User 对象。
    • 特殊 :如果前端发来表单 (a=1&b=2),通常是 Tomcat 转,Converter 不怎么管(除非用 @RequestBody MultiValueMap)。
  2. 处理响应 (Response) ------ 发送

    • 实际动作Java 对象 转 JSON/XML
    • 原理return user; -> Converter (write 方法) -> {"name":"A"} (JSON字符串) -> 发给浏览器。

情况二:Spring Boot 作为客户端 (RestTemplate 远程调用场景)

这是你写 restTemplate.postForObject(...) 去调用别人的时候。

  1. 发起请求 (Request) ------ 发送

    • 实际动作
      • 如果你传 User 对象 -> MappingJackson2HttpMessageConverter 把它转成 JSON 字符串 发出去。
      • 如果你传 MultiValueMap -> FormHttpMessageConverter 把它转成 表单字符串 (a=1&b=2) 发出去。
  2. 接收响应 (Response) ------ 接收

    • 实际动作JSON/XML 转 Java 对象
    • 原理 :别人回给你 {"status":"ok"} -> Converter (read 方法) -> 你拿到 StringResult 对象。

终极总结表(一张表看懂所有方向)

消息转换器 (HttpMessageConverter) 的核心其实只有两个动作:写 (Write)读 (Read)

场景 动作方向 转换逻辑 典型 Converter
Server 响应浏览器 写 (Write) Java 对象 -> JSON/XML Jackson
Client 发送给别人 写 (Write) Java 对象 -> JSON/XML Jackson
Client 发送给别人 写 (Write) MultiValueMap -> 表单String Form
Server 接收浏览器 读 (Read) JSON/XML -> Java 对象 Jackson
Client 接收别人 读 (Read) JSON/XML -> Java 对象 Jackson

处理请求,处理响应,发送请求,接收响应分别都是怎么判断要用哪个转换器的、

Spring 内部维护了一个 HttpMessageConverter 列表 (List)。

无论是 Server 端还是 Client 端,判断"用哪个转换器"的逻辑,本质上都是一个 "遍历列表 + 面试" 的过程。

核心判断标准只有两个要素:

  1. Java 类型 :涉及的 Java 对象是谁?(比如 User.class, String.class, byte[].class
  2. Media 类型 (MIME Type) :涉及的 HTTP 数据格式是什么?(比如 application/json, text/plain

下面我们分这 4 种场景详细拆解:


1. 处理请求 (Server 端读:@RequestBody)

场景:前端发来 POST 请求,Spring MVC 要把 Body 转成 Java 对象。

  • 输入条件

    • Java 目标类型 :Controller 方法参数的类型(如 User.class)。
    • Media 类型HTTP 请求头里的 Content-Type (如 application/json)。
  • 判断逻辑

    Spring 会遍历所有 Converter,挨个问(调用 canRead 方法):

    "喂,你能读取 application/json 格式的数据,并把它转成 User 类型吗?"

    • StringHttpMessageConverter:我看它是 User 类型,我不行。
    • ByteArrayHttpMessageConverter:我看它是 User 类型,我不行。
    • MappingJackson2HttpMessageConverter我可以! (因为它支持 application/json 且支持通用 Object)。
  • 结论:Jackson 胜出,开始干活。


2. 处理响应 (Server 端写:@ResponseBody)

场景 :Controller 返回 Java 对象,Spring MVC 要把它转成 HTTP Body 返回给浏览器。
注意 :这是最复杂的 "内容协商" 过程。

  • 输入条件
    • Java 源类型 :Controller 方法的返回值类型(如 User.class)。
    • Media 类型HTTP 请求头里的 Accept (客户端想要什么,如 application/xml)。
  • 判断逻辑
    1. 统计服务端能力 :Spring 先问一遍所有 Converter:"你们谁能处理 User 类?"
      • Jackson 说:我可以转 JSON。
      • XML Converter 说:我可以转 XML。
    2. 匹配客户端需求 :Spring 拿着服务端能提供的格式(JSON, XML)去跟客户端的 Accept 头做交集。
      • 如果 Accept: application/xml,那就选中 XML Converter。
      • 如果 Accept: */* (都要) 或者没传,通常默认选列表里的第一个(通常是 JSON)。
    3. 最终面试 (调用 canWrite 方法): "XML Converter,你确认你能把 User 对象写成 application/xml 吗?"
  • 结论:协商一致的那个 Converter 胜出。

3. 发送请求 (Client 端写:RestTemplate)

场景 :Java 代码调用 restTemplate.postForObject(url, userObj, ...)

  • 输入条件

    • Java 源类型 :你传入的对象类型(如 User 对象,或者 MultiValueMap)。
    • Media 类型
      • 情况 A:你手动设置了 HttpHeadersContent-Type
      • 情况 B:你没设置(默认情况)。
  • 判断逻辑

    Spring 遍历 Converter 列表,挨个问(调用 canWrite 方法):

    • 如果传的是 User 对象

      • StringConverter:它是 User 类,我不接。
      • FormConverter:它不是 Map,我不接。
      • JacksonConverter:是 Object,我可以接!而且我默认的 Content-Type 是 application/json
      • 结果:Jackson 胜出,发送 JSON。
    • 如果传的是 MultiValueMap

      • JacksonConverter:虽然后面也能处理,但优先级通常较低。
      • FormConverter我就是专门处理 MultiValueMap 的! 我的默认 Content-Type 是 application/x-www-form-urlencoded
      • 结果:FormConverter 胜出,发送表单数据。

4. 接收响应 (Client 端读:RestTemplate)

场景:远程服务器回包了,Java 要把 Body 转成对象。

  • 输入条件

    • Java 目标类型 :你 postForObject 方法里传入的 responseType(如 User.class)。
    • Media 类型远程服务器响应头里的 Content-Type (如 application/json)。
  • 判断逻辑

    Spring 遍历 Converter 列表,挨个问(调用 canRead 方法):

    "喂,收到了一个 application/json 的包,要把他转成 User 类,谁能干?"

    • StringConverter:你要的是 User 类,我只能转 String,我干不了。
    • JacksonConverter:JSON 格式?User 类?正合我意,放着我来!
  • 结论:Jackson 胜出,反序列化对象。


总结一张表

动作 核心依据 (Key Factor 1) 辅助依据 (Key Factor 2) 关键方法
Server 读请求 (@RequestBody) Request Header: Content-Type Controller 参数类型 canRead()
Server 写响应 (@ResponseBody) Request Header: Accept Controller 返回值类型 canWrite()
Client 发请求 (RestTemplate) Java 对象类型 (User/Map) (可选) 手动设置的 Content-Type canWrite()
Client 收响应 (RestTemplate) Response Header: Content-Type 期望的返回值类型 (Class<T>) canRead()

一句话:

  • 读(Read)的时候:看对方发过来的是什么格式(Content-Type),找能懂这个格式的 Converter。
  • 写(Write)的时候:看我要发的是什么对象(Java Type),以及对方想要什么格式(Accept),找个能匹配的 Converter。
相关推荐
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 个人健康管理系统为例,包含答辩的问题和答案
java·spring boot
qq_12498707532 小时前
基于微信小程序的线下点餐系统的设计与实现(源码+论文+部署+安装)
spring boot·微信小程序·小程序·毕业设计
IT_Octopus2 小时前
Java GZip 压缩实践 +实践思考 +进一步压榨性能和存储方案思考:Protobuf+ GZip
java·spring boot
毕设源码-郭学长2 小时前
【开题答辩全过程】以 高校教材大管家系统为例,包含答辩的问题和答案
java·spring boot
qq_12498707533 小时前
基于SpringBoot+vue的小黄蜂外卖平台(源码+论文+部署+安装)
java·开发语言·vue.js·spring boot·后端·mysql·毕业设计
i02083 小时前
Java 17 + Spring Boot 3.2.5 使用 Redis 实现“生产者–消费者”任务队列
java·spring boot·redis
5***b974 小时前
SpringBoot(整合MyBatis + MyBatis-Plus + MyBatisX插件使用)
spring boot·tomcat·mybatis
卷到起飞的数分4 小时前
22.Maven高级——继承与聚合
服务器·spring boot
P***84394 小时前
idea创建springBoot的五种方式
java·spring boot·intellij-idea