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

为了彻底搞清,我们把 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。
相关推荐
高级盘丝洞13 小时前
Spring Boot 集成 InfluxDB 3.x
spring boot
宋情写14 小时前
Springboot基础篇01-创建一个SpringBoot项目
java·spring boot·后端
用户83071968408214 小时前
Spring ApplicationEventPublisher 异步处理与消息队列全面解析
spring boot·rabbitmq
这是程序猿14 小时前
基于java的SpringBoot框架汽车销售系统
java·spring boot·spring·汽车·汽车销售网站
ONExiaobaijs15 小时前
基于Spring Boot的校园闲置物品交易系统
java·spring boot·后端
码界奇点15 小时前
基于Spring Boot和Vue的多通道支付网关系统设计与实现
vue.js·spring boot·后端·毕业设计·鸿蒙系统·源代码管理
IT 行者15 小时前
Spring Boot 升级之HTTP客户端调整:HttpExchange 与 Feign Client 深度对比分析
spring boot·后端·http
小蒜学长15 小时前
python基于Python的医疗机构药品及耗材信息管理系统(代码+数据库+LW)
数据库·spring boot·后端·python
九月生15 小时前
Spring Boot 自动装配原理深度剖析:以集成 Redis 为例
spring boot·redis
invicinble15 小时前
Spring Boot 内嵌 Tomcat 处理 HTTP 请求的全过程
spring boot·http·tomcat