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

为了彻底搞清,我们把 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。
相关推荐
韩立学长1 小时前
基于Springboot流浪动物救助系统o8g44kwc(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
qq_318121591 小时前
互联网大厂Java面试故事:在线教育微服务架构、缓存优化与AI智能教学全流程解析
java·spring boot·redis·微服务·kafka·spring security·在线教育
大猫和小黄3 小时前
Java异常处理:从基础到SpringBoot实战解析
java·开发语言·spring boot·异常
hero.fei3 小时前
kaptcha 验证码生成工具在springboot中集成
java·spring boot·后端
w***76554 小时前
存储技术全景:从基础原理到未来趋势
spring boot·后端·mybatis
Miss_Chenzr5 小时前
Springboot企业人事管理系统mi130(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·spring boot
风景的人生7 小时前
springboot项目用maven插件打包时候报错
java·spring boot·maven
vx_bisheyuange7 小时前
基于SpringBoot的经方药食服务平台
java·spring boot·后端·毕业设计
哈哈老师啊7 小时前
Springboot企业办公信息化管理系统6z1v1(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
阿杰真不会敲代码8 小时前
POI 讲解
java·spring boot