HttpMessageConverter 注册体系
概述
HttpMessageConverter 是 Spring MVC 中 HTTP 请求/响应体序列化与反序列化的核心接口。Spring Boot 通过 HttpMessageConvertersAutoConfiguration 自动注册一组内建转换器,并通过 HttpMessageConverters 统一管理,支持用户自定义转换器的追加与排序。
本文将深入拆解 HttpMessageConverter 注册体系中的 8 个细节点。
本文基于 Spring Boot 3.x + Spring MVC 6.x 源码分析。
1. HttpMessageConverters 的 4 注册组
1.1 源码
java
// HttpMessageConverters.java
public class HttpMessageConverters
implements Iterable<HttpMessageConverter<?>> {
private final List<HttpMessageConverter<?>> converters;
public HttpMessageConverters(
// 用户通过 @Bean 注册的自定义转换器
HttpMessageConverter<?>... additionalConverters) {
this(true, Arrays.asList(additionalConverters));
}
public HttpMessageConverters(
boolean addDefaultConverters,
Collection<HttpMessageConverter<?>> additionalConverters) {
// 构建转换器列表
List<HttpMessageConverter<?>> converters = new ArrayList<>();
// 1. 追加用户自定义转换器(添加到链首)
if (additionalConverters != null) {
converters.addAll(additionalConverters);
}
if (addDefaultConverters) {
// 2. 添加默认转换器
converters.addAll(new DefaultConverterConfiguration()
.getDefaultConverters());
// 3. 添加默认候补转换器(仅在 classpath 存在时添加)
converters.addAll(
new DefaultConverterConfiguration().getExtraConverters());
}
// 4. 排序: 自定义转换器始终在默认转换器之前
this.converters = Collections.unmodifiableList(
sortConverters(converters));
}
}1.2 四个注册组的详细内容
java
// 组 1: 用户自定义转换器(通过 @Bean 注册)
// @Configuration
// public class MyConverterConfig {
// @Bean
// public HttpMessageConverter<?> customConverter() {
// return new MyCustomConverter();
// }
// }
// → 追加到转换器链首
// 组 2: 默认转换器(始终添加)
// ByteArrayHttpMessageConverter — 处理 application/octet-stream
// StringHttpMessageConverter — 处理 text/plain (*/*)
// ResourceHttpMessageConverter — 处理 application/octet-stream
// ResourceRegionHttpMessageConverter — 处理部分资源请求
// SourceHttpMessageConverter — 处理 application/xml
// AllEncompassingFormHttpMessageConverter — 处理 multipart/form-data
// 以及 MappingJackson2HttpMessageConverter 等 JSON/XML 转换器
// 组 3: 默认候补转换器(按 classpath 检测)
// MappingJackson2HttpMessageConverter — jackson-databind 存在时
// MappingJackson2XmlHttpMessageConverter — jackson-dataformat-xml 存在时
// GsonHttpMessageConverter — gson 存在时
// JsonbHttpMessageConverter — javax.json.bind-api 存在时
// 注意: 这些候补按优先级只有一个生效
// 组 4: 排序后的完整列表
// 自定义 → ByteArray → String → Resource → ... → JSON 转换器2. StringHttpMessageConverter 的字符集处理
2.1 源码
java
// StringHttpMessageConverter.java
public class StringHttpMessageConverter
extends AbstractHttpMessageConverter<String> {
// 默认字符集
public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
// 支持的所有 Content-Type
// text/plain 和 */*
@Override
public boolean supports(Class<?> clazz) {
// 只支持 String 类型
return String.class == clazz;
}
@Override
protected void writeInternal(String str, HttpOutputMessage outputMessage)
throws IOException {
HttpHeaders headers = outputMessage.getHeaders();
// 1. 确定写入字符集
// 优先级: Content-Type 头中指定的 charset > 默认 UTF-8
Charset charset = StandardCharsets.UTF_8;
MediaType contentType = headers.getContentType();
if (contentType != null && contentType.getCharset() != null) {
charset = contentType.getCharset();
}
// 2. 写入响应体
// 使用指定的字符集编码为字节数组
byte[] bytes = str.getBytes(charset);
// 3. 设置 Content-Type(如果未设置)
if (headers.getContentType() == null) {
if (charset.equals(StandardCharsets.UTF_8)) {
headers.setContentType(
new MediaType("text", "plain", charset));
} else {
headers.setContentType(
new MediaType("text", "plain"));
}
}
// 4. 写入 OutputStream
StreamUtils.copy(bytes, outputMessage.getBody());
}
@Override
protected String readInternal(Class<? extends String> clazz,
HttpInputMessage inputMessage) throws IOException {
// 1. 获取请求体的 Content-Length
long contentLength = inputMessage.getHeaders().getContentLength();
// 2. 确定读取字符集
// 优先级: Content-Type 头的 charset > 默认 ISO-8859-1
Charset charset = getContentTypeCharset(
inputMessage.getHeaders().getContentType());
// 3. 从 InputStream 读取字符串
if (contentLength >= 0) {
// 已知长度: 读取指定字节数
byte[] bytes = new byte[(int) contentLength];
StreamUtils.readBytes(inputMessage.getBody(), bytes);
return new String(bytes, charset);
} else {
// 未知长度: 读取所有字节
ByteArrayOutputStream bos = new ByteArrayOutputStream();
StreamUtils.copy(inputMessage.getBody(), bos);
return new String(bos.toByteArray(), charset);
}
}
}2.2 字符集处理的完整链路
请求 → 响应
│
├─ 读取请求体(readInternal)
│ │
│ ├─ 检查 Content-Type 头
│ │ text/plain; charset=UTF-8 → 使用 UTF-8
│ │ text/plain → 使用 ISO-8859-1(默认)
│ │ */* → 使用 ISO-8859-1
│ │
│ └─ 读取 InputStream → String(charset)
│
└─ 写入响应体(writeInternal)
│
├─ 检查 Content-Type 头
│ 如果已设置 charset → 使用指定字符集
│ 如果未设置 charset → 使用 UTF-8(写操作默认)
│
└─ String.getBytes(charset) → OutputStream2.3 Spring Boot 3.x 的变化
java
// Spring Boot 3.x 中 StringHttpMessageConverter 默认使用 UTF-8
// 不再是历史版本的 ISO-8859-1
// Spring Boot 自动配置:
@Bean
@ConditionalOnMissingBean
StringHttpMessageConverter stringHttpMessageConverter() {
// 默认使用 UTF-8 编码
StringHttpMessageConverter converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
// 设置默认 Content-Type 的字符集
converter.setWriteAcceptCharset(false);
return converter;
}3. MappingJackson2HttpMessageConverter.write() 序列化流程
3.1 源码
java
// MappingJackson2HttpMessageConverter.java
public class MappingJackson2HttpMessageConverter
extends AbstractJackson2HttpMessageConverter {
public MappingJackson2HttpMessageConverter() {
// 默认使用 Jackson ObjectMapper
this(Jackson2ObjectMapperBuilder.json().build());
}
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// 使用 ObjectMapper 写入
// 委托给父类 AbstractJackson2HttpMessageConverter
super.writeInternal(object, outputMessage);
}
}
// AbstractJackson2HttpMessageConverter.java
public abstract class AbstractJackson2HttpMessageConverter
extends AbstractGenericHttpMessageConverter<Object> {
protected void writeInternal(Object value, HttpOutputMessage outputMessage)
throws IOException {
// 1. 获取 OutputStream
OutputStream outputStream = outputMessage.getBody();
// 2. 包装为 JsonGenerator(或特殊处理)
JsonEncoding encoding = getJsonEncoding(
outputMessage.getHeaders().getContentType());
// 3. 处理 Java 类型
// 如果是泛型类型,使用 JavaType
// 否则使用普通 Class
Class<?> clazz = (value instanceof Class ? (Class<?>) value : value.getClass());
// 4. 创建 JsonGenerator
JsonGenerator generator = objectMapper.getFactory()
.createGenerator(outputStream, encoding);
// 5. 处理序列化特性
boolean wrap = objectMapper.getSerializationConfig()
.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
// 6. 使用 ObjectMapper 序列化
if (wrap) {
// 包装根值(@JsonRootName)
objectMapper.writer().withRootName(clazz)
.writeValue(generator, value);
} else {
// 普通序列化
objectMapper.writeValue(generator, value);
}
// 7. 刷新并关闭 generator
generator.flush();
}
}3.2 完整的序列化流程
调用链:
controller 方法返回 @ResponseBody User 对象
│
├─ RequestResponseBodyMethodProcessor.handleReturnValue()
│ └─ AbstractMessageConverterMethodArgumentResolver
│ └─ writeWithMessageConverters()
│ │
│ ├─ 1. 遍历所有 HttpMessageConverter
│ │ ├─ canWrite(User.class, application/json)
│ │ │ MappingJackson2HttpMessageConverter:
│ │ │ → supports(Class): 检查泛型类型
│ │ │ → canWrite(MediaType): 检查 application/json
│ │ │ → 匹配成功
│ │ │
│ ├─ 2. 设置 Content-Type
│ │ └─ response.setContentType(MediaType.APPLICATION_JSON)
│ │
│ ├─ 3. 调用 writeInternal()
│ │ └─ ObjectMapper.writeValue(generator, user)
│ │ │
│ │ ├─ 获取 SerializationConfig
│ │ ├─ 创建 SerializerProvider
│ │ ├─ 查找 User 的序列化器
│ │ ├─ 序列化字段
│ │ │ ├─ id → serializeNumber(1)
│ │ │ ├─ name → serializeString("John")
│ │ │ └─ email → serializeString("john@example.com")
│ │ └─ 写入 JsonGenerator
│ │
│ └─ 4. 输出 JSON 到 OutputStream
│ └─ {"id":1,"name":"John","email":"john@example.com"}
│
└─ 响应返回客户端4. MappingJackson2HttpMessageConverter.read() 反序列化流程
4.1 源码
java
// AbstractJackson2HttpMessageConverter.java
@Override
public Object read(Type type, Class<?> contextClass,
HttpInputMessage inputMessage) throws IOException {
// 1. 获取 JavaType(泛型类型处理)
JavaType javaType = getJavaType(type, contextClass);
// 2. 读取请求体
return readJavaType(javaType, inputMessage);
}
private Object readJavaType(JavaType javaType,
HttpInputMessage inputMessage) throws IOException {
// 1. 获取 InputStream
InputStream inputStream = inputMessage.getBody();
// 2. 确定字符集(从 Content-Type 头获取)
MediaType contentType = inputMessage.getHeaders().getContentType();
JsonEncoding encoding = getJsonEncoding(contentType);
// 3. 创建 JsonParser
JsonParser parser = objectMapper.getFactory()
.createParser(inputStream);
// 4. 处理 JSON 根值包装
boolean rootWrapping = objectMapper.getDeserializationConfig()
.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE);
// 5. 反序列化
Object value;
if (rootWrapping) {
// 如果 JSON 包含根包装(@JsonRootName)
// {"user":{"id":1,"name":"John"}} → User 对象
value = objectMapper.readValue(parser, javaType);
} else {
// 普通反序列化
value = objectMapper.readValue(parser, javaType);
}
// 6. 关闭 parser
parser.clearCurrentToken();
return value;
}4.2 完整的反序列化流程
请求体: {"id":1,"name":"John","email":"john@example.com"}
│
├─ controller 方法 @RequestBody User user 接收
│
├─ RequestResponseBodyMethodProcessor.resolveArgument()
│ └─ readWithMessageConverters()
│ │
│ ├─ 1. 遍历所有 HttpMessageConverter
│ │ ├─ canRead(User.class, application/json)
│ │ │ MappingJackson2HttpMessageConverter:
│ │ │ → canRead(): 检查类是否可反序列化
│ │ │ → supports(): 检查泛型类型
│ │ │ → 匹配成功
│ │ │
│ ├─ 2. 调用 read()
│ │ └─ ObjectMapper.readValue(parser, User.class)
│ │ │
│ │ ├─ 创建 JsonParser
│ │ ├─ 查找 User 的反序列化器
│ │ ├─ 反序列化字段
│ │ │ ├─ id → deserializeNumber → 1
│ │ │ ├─ name → deserializeString → "John"
│ │ │ └─ email → deserializeString → "john@example.com"
│ │ └─ 创建 User 对象(无参构造 + setter / 构造器绑定)
│ │
│ └─ 3. 返回 User 对象给控制器
│
└─ controller 中使用 User 对象5. AllEncompassingFormHttpMessageConverter 表单处理
5.1 源码
java
// AllEncompassingFormHttpMessageConverter.java
public class AllEncompassingFormHttpMessageConverter
extends FormHttpMessageConverter {
public AllEncompassingFormHttpMessageConverter() {
// 1. 设置支持的 MediaType
// application/x-www-form-urlencoded
// multipart/form-data
// multipart/mixed(部分表单)
// 2. 注册默认 Part 转换器
// StringHttpMessageConverter — 字符串字段
// ByteArrayHttpMessageConverter — 二进制文件
// ResourceHttpMessageConverter — 资源文件
// 3. 条件注册额外的 Part 转换器
// 如果 classpath 存在:
// jackson-databind → MappingJackson2HttpMessageConverter
// gson → GsonHttpMessageConverter
// javax.json.bind → JsonbHttpMessageConverter
}
}
// FormHttpMessageConverter.java
public class FormHttpMessageConverter
implements HttpMessageConverter<MultiValueMap<String, ?>> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
// 只能处理 MultiValueMap
if (!MultiValueMap.class.isAssignableFrom(clazz)) {
return false;
}
// 支持的 MediaType:
// application/x-www-form-urlencoded
// multipart/form-data
if (mediaType == null) {
return false;
}
return SUPPORTED_MEDIA_TYPES.contains(
new MediaType(mediaType.getType(), mediaType.getSubtype()));
}
@Override
public MultiValueMap<String, String> read(Class<? extends
MultiValueMap<String, ?>> clazz, HttpInputMessage inputMessage)
throws IOException {
// 处理 application/x-www-form-urlencoded 格式
// 读取请求体 → 解析 form 参数
MediaType contentType = inputMessage.getHeaders().getContentType();
if (MediaType.APPLICATION_FORM_URLENCODED.includes(contentType)) {
// 表单格式: key1=value1&key2=value2
return readForm(inputMessage);
}
// multipart 格式由 MultipartResolver 处理
// 这里不直接处理 multipart
throw new HttpMessageNotReadableException(
"Content type [" + contentType + "] not supported");
}
@Override
public void write(MultiValueMap<String, ?> map, MediaType contentType,
HttpOutputMessage outputMessage) throws IOException {
// 判断是否是 multipart
if (isMultipart(map, contentType)) {
// 处理 multipart/form-data(包含文件上传)
writeMultipart(map, outputMessage);
} else {
// 处理 application/x-www-form-urlencoded
writeForm(map, outputMessage);
}
}
}5.2 表单处理的两种场景
java
// 场景 1: 普通表单(application/x-www-form-urlencoded)
// POST /submit
// Content-Type: application/x-www-form-urlencoded
// name=John&email=john@example.com
//
// 读取:
// AllEncompassingFormHttpMessageConverter.read()
// → readForm(): 逐对解析 key=value
// → 返回 MultiValueMap<String, String>
// { "name": ["John"], "email": ["john@example.com"] }
// 场景 2: Multipart 表单(multipart/form-data)
// POST /upload
// Content-Type: multipart/form-data; boundary=----xxx
// ------xxx
// Content-Disposition: form-data; name="file"; filename="photo.jpg"
// Content-Type: image/jpeg
//
// [二进制数据]
// ------xxx
// Content-Disposition: form-data; name="description"
//
// 一张风景照
// ------xxx--
//
// 处理这个请求的是:
// StandardServletMultipartResolver(MultipartResolver)
// 不是 AllEncompassingFormHttpMessageConverter
//
// AllEncompassingFormHttpMessageConverter 只负责
// 将 MultiValueMap 序列化为 multipart/form-data 响应6. @ResponseBody 注解的处理
6.1 RequestResponseBodyMethodProcessor 的角色
java
// RequestResponseBodyMethodProcessor.java
public class RequestResponseBodyMethodProcessor
extends AbstractMessageConverterMethodProcessor {
// 处理 @ResponseBody(响应)
@Override
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
// 1. 标记请求已处理(不返回视图)
mavContainer.setRequestHandled(true);
// 2. 获取 MessageConverter 参数
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// 3. 委托 writeWithMessageConverters()
writeWithMessageConverters(returnValue, returnType,
inputMessage, outputMessage);
}
// 处理 @RequestBody(请求)
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 1. 创建输入消息
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
// 2. 委托 readWithMessageConverters()
Object arg = readWithMessageConverters(inputMessage, parameter,
parameter.getNestedGenericParameterType());
// 3. 处理可选类型(Optional)
if (parameter.isOptional()) {
arg = Optional.ofNullable(arg);
}
return arg;
}
}6.2 writeWithMessageConverters() 选择转换器
java
// AbstractMessageConverterMethodProcessor.java
protected <T> void writeWithMessageConverters(T value,
MethodParameter returnType,
ServletServerHttpRequest inputMessage,
ServletServerHttpResponse outputMessage) throws IOException {
// 1. 确定目标类型
Class<?> valueType = value.getClass();
Type declaredType = GenericTypeResolver.resolveType(
returnType.getGenericParameterType(), returnType.getContainingClass());
// 2. 确定可接受的 MediaType
// 从请求 Accept 头: Accept: application/json
// 从 produces 属性: @GetMapping(produces = "application/json")
// 从转换器支持的 MediaType
List<MediaType> acceptableMediaTypes = getAcceptableMediaTypes(request);
// 3. 遍历可接受的 MediaType
for (MediaType requestedType : acceptableMediaTypes) {
// 4. 遍历所有 HttpMessageConverter
for (HttpMessageConverter<?> converter : this.messageConverters) {
// 5. 检查转换器是否支持
GenericHttpMessageConverter genericConverter =
(converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null) {
// 泛型转换器检查
if (genericConverter.canWrite(declaredType, valueType, requestedType)) {
// 匹配成功 → 写入响应
genericConverter.write(value, declaredType, requestedType, outputMessage);
return;
}
} else {
// 普通转换器检查
if (converter.canWrite(valueType, requestedType)) {
((HttpMessageConverter<T>) converter).write(value, requestedType, outputMessage);
return;
}
}
}
}
// 6. 没有找到合适的转换器 → 抛出异常
throw new HttpMediaTypeNotAcceptableException(
this.allSupportedMediaTypes);
}6.3 完整的 @ResponseBody 处理流程
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // 返回 User 对象
}
}
│
├─ 1. DispatcherServlet 调用 HandlerAdapter.handle()
│ → RequestMappingHandlerAdapter.invokeHandlerMethod()
│
├─ 2. 找到匹配的 HandlerMethod
│ → InvocableHandlerMethod.invokeForRequest()
│ → 执行 getUser() 方法
│
├─ 3. ReturnValueHandler 处理返回值
│ → RequestResponseBodyMethodProcessor.handleReturnValue()
│
├─ 4. writeWithMessageConverters()
│ ├─ Accept: application/json(来自请求头)
│ ├─ MappingJackson2HttpMessageConverter.canWrite(User, application/json)
│ │ → MediaType: application/json 匹配
│ │ → User 类可序列化
│ │ → 返回 true
│ └─ MappingJackson2HttpMessageConverter.write(user, application/json, response)
│
├─ 5. 设置响应头
│ Content-Type: application/json
│
├─ 6. writeInternal()
│ ObjectMapper.writeValue() → JSON 字符串
│
└─ 7. 输出到 OutputStream
{"id":1,"name":"John","email":"john@example.com"}7. 转换器 canRead() / canWrite() 的匹配
7.1 源码
java
// GenericHttpMessageConverter 接口
public interface GenericHttpMessageConverter<T>
extends HttpMessageConverter<T> {
// 扩展的 canWrite 和 canRead 方法
// 支持泛型类型判断
boolean canWrite(Type type, Class<?> contextClass, MediaType mediaType);
boolean canRead(Type type, Class<?> contextClass, MediaType mediaType);
}
// AbstractGenericHttpMessageConverter.java
public abstract class AbstractGenericHttpMessageConverter<T>
extends AbstractHttpMessageConverter<T>
implements GenericHttpMessageConverter<T> {
@Override
public boolean canWrite(Type type, Class<?> contextClass,
MediaType mediaType) {
// 1. 检查 Java 类型是否支持
if (!supports(type)) {
return false;
}
// 2. 获取 JavaType(解析泛型)
JavaType javaType = getJavaType(type, contextClass);
// 3. 检查 ObjectMapper 是否可以序列化
if (!jacksonCanSerialize(javaType)) {
return false;
}
// 4. 检查 MediaType 是否匹配
if (mediaType == null) {
// 如果没有指定 MediaType → 只要可序列化就返回 true
return true;
}
// 5. 检查 MediaType 的匹配
// 例如 MappingJackson2HttpMessageConverter 支持:
// application/json
// application/*+json
for (MediaType supported : getSupportedMediaTypes()) {
if (supported.includes(mediaType)) {
return true;
}
}
return false;
}
}7.2 匹配条件对照表
| 转换器 | canRead/canWrite 条件 | 支持的 MediaType |
|---|---|---|
ByteArrayHttpMessageConverter | 类型是 byte[] | application/octet-stream, */* |
StringHttpMessageConverter | 类型是 String | text/plain, */* |
ResourceHttpMessageConverter | 类型是 Resource | application/octet-stream, */* |
MappingJackson2HttpMessageConverter | 类型非基本类型 + 可序列化 | application/json, application/*+json |
MappingJackson2XmlHttpMessageConverter | 类型非基本类型 + 可序列化 | application/xml, text/xml, application/*+xml |
GsonHttpMessageConverter | 类型非基本类型 + Gson 可序列化 | application/json, application/*+json |
AllEncompassingFormHttpMessageConverter | 类型是 MultiValueMap | application/x-www-form-urlencoded, multipart/form-data |
7.3 canRead() 的匹配顺序
@RequestMapping(produces = "application/json")
public User getUser() { ... }
请求: Accept: application/xml, application/json
匹配过程:
1. 遍历 acceptableMediaTypes: [application/xml, application/json]
2. 遍历 messageConverters:
├─ ByteArrayHttpMessageConverter → canWrite(User, application/xml) → false
├─ StringHttpMessageConverter → canWrite(User, application/xml) → false
├─ MappingJackson2XmlHttpMessageConverter → canWrite(User, application/xml) → true
│ → 使用 Xml 转换器输出 User
└─ (如果没有 xml 转换器)
MappingJackson2HttpMessageConverter → canWrite(User, application/json) → true
→ 使用 Json 转换器输出 User8. HttpMessageConverters 追加/排序逻辑
8.1 源码
java
// HttpMessageConverters.java 的排序逻辑
private List<HttpMessageConverter<?>> sortConverters(
List<HttpMessageConverter<?>> converters) {
// 1. 分离默认转换器和自定义转换器
List<HttpMessageConverter<?>> result = new ArrayList<>(converters);
// 2. 排序策略: 使用 AnnotationAwareOrderComparator
// 处理 @Order 和 Ordered 接口
AnnotationAwareOrderComparator.sort(result);
// 3. 自定义转换器的优先级
// 如果没有标注 @Order,自定义转换器默认排在默认转换器前面
// 因为 HttpMessageConverters 的自定义转换器在创建时先加入列表
// 4. 最终顺序(以 JSON 为例):
// ① 自定义转换器(如果有 @Order = Ordered.HIGHEST_PRECEDENCE 则最前)
// ② 默认转换器(ByteArray、String、Resource、Source 等)
// ③ JSON 转换器(MappingJackson2HttpMessageConverter 等)
return result;
}8.2 自定义转换器的追加方式
java
// 方式 1: 定义 HttpMessageConverter @Bean(推荐)
@Configuration
public class MyConverterConfig {
@Bean
public HttpMessageConverter<?> customConverter1() {
// 自定义转换器
// 自动被 HttpMessageConvertersAutoConfiguration 收集
return new MyCustomConverter();
}
@Bean
public HttpMessageConverter<?> customConverter2() {
return new AnotherConverter();
}
}
// 方式 2: 实现 WebMvcConfigurer.configureMessageConverters()
// 完全控制转换器列表(会覆盖默认的)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
// 完全替换默认转换器
converters.add(new MyCustomConverter());
converters.add(new StringHttpMessageConverter());
// 不会添加默认的 Boot 转换器
}
}
// 方式 3: 实现 WebMvcConfigurer.extendMessageConverters()
// 保留默认转换器,追加自定义转换器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(
List<HttpMessageConverter<?>> converters) {
// 在默认转换器后面追加
converters.add(new MyCustomConverter());
}
}8.3 三种方式的优先级对比
方式 1: @Bean HttpMessageConverter
│
├─ 通过 HttpMessageConvertersAutoConfiguration 收集
├─ 添加到 HttpMessageConverters 的自定义组
├─ 排在默认转换器之前
└─ 可以被多个 @Bean 追加
方式 2: configureMessageConverters()(完全控制)
│
├─ 完全替换默认转换器
├─ converters 中只包含你添加的
└─ 需要手动添加所有需要的转换器
方式 3: extendMessageConverters()(追加)
│
├─ 在方式 1/2 的 converters 基础上追加
├─ 所有默认转换器已存在
└─ 适合添加少量自定义转换器
建议: 大多数场景使用 方式 1(@Bean),简单且可组合8.4 完整转换器链示例
java
// Spring Boot 默认的 HttpMessageConverter 链(按顺序)
// [0] 用户自定义转换器(如果有 @Bean)
// [1] ByteArrayHttpMessageConverter
// 支持: application/octet-stream
// 作用: 处理 byte[] 类型
// [2] StringHttpMessageConverter
// 支持: text/plain, */*
// 作用: 处理 String 类型
// [3] ResourceHttpMessageConverter
// 支持: application/octet-stream, */*
// 作用: 处理 Resource(文件下载)
// [4] ResourceRegionHttpMessageConverter
// 支持: application/octet-stream
// 作用: 处理部分资源请求(断点续传)
// [5] SourceHttpMessageConverter
// 支持: application/xml, text/xml, application/*+xml
// 作用: 处理 javax.xml.transform.Source
// [6] AllEncompassingFormHttpMessageConverter
// 支持: application/x-www-form-urlencoded, multipart/form-data
// 作用: 处理表单提交
// [7] MappingJackson2HttpMessageConverter(classpath 有 jackson-databind)
// 支持: application/json, application/*+json
// 作用: 处理 JSON
// [8] MappingJackson2XmlHttpMessageConverter(classpath 有 jackson-dataformat-xml)
// 支持: application/xml, text/xml, application/*+xml
// 作用: 处理 XML总结
| # | 细节点 | 核心要点 |
|---|---|---|
| ① | 4 注册组 | 用户自定义 + 默认转换器 + 候补转换器(按 classpath) + 排序后的完整列表 |
| ② | StringHttpMessageConverter 字符集 | 读: 默认 ISO-8859-1,写: 默认 UTF-8,Spring Boot 3.x 统一 UTF-8 |
| ③ | 序列化流程 | canWrite() → ObjectMapper.writeValue() → JsonGenerator → OutputStream |
| ④ | 反序列化流程 | canRead() → ObjectMapper.readValue() → JavaType 类型推断 → JsonParser |
| ⑤ | 表单处理 | 普通表单(application/x-www-form-urlencoded)逐对解析;multipart 由 MultipartResolver 处理 |
| ⑥ | @ResponseBody | RequestResponseBodyMethodProcessor 遍历转换器链,匹配 canWrite() + Accept 头 |
| ⑦ | canRead()/canWrite() | 检查类型支持 + supports() + MediaType 匹配 + 泛型类型解析 |
| ⑧ | 追加/排序 | @Bean 转换器排在链首,@Order/Ordered 控制内部排序 |