Spring MVC:HttpMessageConverter

HttpMessageConverter 是 Spring MVC(以及 Spring WebFlux)中用于在 HTTP 请求/响应与 Java 对象之间进行转换的核心接口。它解决了 HTTP 协议传输的是字节流,而 Java 业务代码处理的是对象这一矛盾,实现了数据的序列化(Object -> HTTP Body)和反序列化(HTTP Body -> Object)

以下是关于 messageConverters 的核心机制、默认实现、匹配规则及自定义配置的详细解析。

1. 核心职责与接口定义

HttpMessageConverter 接口定义了五个关键方法,决定了转换器是否能处理特定类型的数据以及如何读写数据:
public interface HttpMessageConverter<T> {
    // 1. 判断是否支持将 HTTP 请求体读取为指定类型的 Java 对象
    boolean canRead(Class<?> clazz, MediaType mediaType);

    // 2. 判断是否支持将指定类型的 Java 对象写入 HTTP 响应体
    boolean canWrite(Class<?> clazz, MediaType mediaType);

    // 3. 获取该转换器支持的媒体类型列表(如 application/json, text/plain)
    List<MediaType> getSupportedMediaTypes();

    // 4. 从输入消息中读取并转换为对象(反序列化)
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
        throws IOException, HttpMessageNotReadableException;

    // 5. 将对象写入输出消息(序列化)
    void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException;
}


主要应用场景:‌

@RequestBody‌:Spring 使用 canRead 和 read 方法将请求体 JSON/XML 转换为 Controller 方法的参数对象。
@ResponseBody / ResponseEntity‌:Spring 使用 canWrite 和 write 方法将 Controller 返回的对象序列化为 JSON/XML 写入响应体。
2. 默认注册的转换器

在 Spring Boot 或启用 <mvc:annotation-driven/> 的环境中,RequestMappingHandlerAdapter 会自动注册一组默认的 HttpMessageConverter。常见的默认实现包括:

转换器类名支持格式依赖库说明
ByteArrayHttpMessageConverterapplication/octet-stream处理 byte[]数组
StringHttpMessageConvertertext/plain, */ *处理 String 类型,默认 UTF-8
ResourceHttpMessageConverter*/*处理 Resource 类型(如文件下载)
SourceHttpMessageConverterapplication/xmlJDK XML处理 javax.xml.transform.Source
AllEncompassingFormHttpMessageConverterapplication/x-www-form-urlencoded处理表单数据
MappingJackson2HttpMessageConverterapplication/jsonJackson‌最常用‌,处理 JSON 转换
Jaxb2RootElementHttpMessageConverterapplication/xmlJAXB处理 XML 转换(若 classpath 存在 JAXB)

注意‌:MappingJackson2HttpMessageConverter 是前后端分离开发中最核心的转换器,只要项目中引入了 jackson-databind 依赖,Spring Boot 就会自动配置它

3. 匹配与执行流程

当请求到达 Controller 时,RequestMappingHandlerAdapter 会遍历已注册的 messageConverters 列表,遵循以下逻辑:

读取阶段(Request -> Object)‌:

检查请求头的 Content-Type。
遍历转换器,调用 canRead(targetClass, mediaType)。
第一个返回 true 的转换器将被选中,执行 read() 方法。
若无匹配转换器,抛出 HttpMediaTypeNotSupportedException (415)。

写入阶段(Object -> Response)‌:

检查请求头的 Accept(内容协商)或 @RequestMapping 中的 produces 属性。
遍历转换器,调用 canWrite(returnValue.getClass(), mediaType)。
第一个返回 true 的转换器将被选中,执行 write() 方法。
若无匹配转换器,抛出 HttpMediaTypeNotAcceptableException (406)。

关键点‌:转换器的‌注册顺序‌非常重要。Spring 默认按顺序查找,一旦找到匹配的即停止。因此,自定义转换器通常需要通过配置调整顺序,或者确保其 canRead/canWrite 逻辑足够精确,避免拦截默认行为。

4. 自定义 HttpMessageConverter

若需支持非标准格式(如自定义二进制协议、特定加密格式等),可实现 HttpMessageConverter 接口。

示例:自定义转换器
public class CustomTextMessageConverter implements HttpMessageConverter<String> {

    private static final MediaType CUSTOM_MEDIA_TYPE = new MediaType("application", "custom-text");

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return String.class.isAssignableFrom(clazz) && CUSTOM_MEDIA_TYPE.includes(mediaType);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return String.class.isAssignableFrom(clazz) && CUSTOM_MEDIA_TYPE.includes(mediaType);
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return Collections.singletonList(CUSTOM_MEDIA_TYPE);
    }

    @Override
    public String read(Class<? extends String> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        // 自定义读取逻辑,例如解密
        InputStream body = inputMessage.getBody();
        return new String(body.readAllBytes(), StandardCharsets.UTF_8).toUpperCase();
    }

    @Override
    public void write(String str, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        // 自定义写入逻辑,例如加密
        OutputStream body = outputMessage.getBody();
        body.write(("Custom: " + str).getBytes(StandardCharsets.UTF_8));
    }
}

注册自定义转换器

在 Spring Boot 中,推荐通过实现 WebMvcConfigurer 接口来注册,这样可以保留默认转换器,仅添加新的转换器或调整顺序。
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 方式1:添加到列表末尾(优先级最低)
        converters.add(new CustomTextMessageConverter());
        
        // 方式2:添加到列表头部(优先级最高,可能覆盖默认行为,需谨慎)
        // converters.add(0, new CustomTextMessageConverter());
    }
    
    // 注意:如果使用 extendMessageConverters,可以访问并修改默认列表
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 例如:移除默认的 StringHttpMessageConverter,替换为自定义的
        // converters.removeIf(c -> c instanceof StringHttpMessageConverter);
        // converters.add(new CustomStringConverter());
    }
}

5. 常见问题与排查

返回字符串带双引号‌:

现象‌:Controller 返回 String,前端收到 "hello" 而不是 hello。
原因‌:MappingJackson2HttpMessageConverter 优先级高于 StringHttpMessageConverter,它将 String 当作 JSON 字符串处理,因此加上了引号。
解决‌:调整转换器顺序,确保 StringHttpMessageConverter 在 Jackson 之前;或在 Controller 上设置 produces = "text/plain"。

415 Unsupported Media Type‌:

原因‌:请求头 Content-Type 与任何转换器的 canRead 不匹配。例如,前端发送 application/json,但后端缺少 Jackson 依赖或转换器未正确注册。

406 Not Acceptable‌:

原因‌:响应头 Accept 要求的内容格式,后端没有任何转换器支持 canWrite。例如,前端要求 application/xml,但后端只配置了 JSON 转换器。

RestTemplate 中的 NPE‌:

若在非 Spring Boot 环境手动创建 RestTemplate,需确保调用了 restTemplate.setMessageConverters(...) 或使用了带默认构造器的变体,否则可能因转换器列表为空导致空指针异常。
总结

messageConverters 是 Spring MVC 实现 RESTful 风格和数据绑定的基石。理解其匹配机制(canRead/canWrite)和执行顺序,对于解决数据格式错误、自定义协议支持以及性能优化至关重要。在大多数常规开发中,默认的 Jackson 转换器已能满足需求;仅在处理特殊数据格式时,才需要自定义实现。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值