HTTP消息转换器MappingJackson2HttpMessageConverter生效的几种方式。

1,348 阅读3分钟

简介:

MappingJackson2HttpMessageConverter是一个Spring消息转换器,用于在Web应用程序中处理请求和响应内容。它的工作原理如下:

  1. 请求处理:当接收到请求时,MappingJackson2HttpMessageConverter将请求内容(通常是JSON格式)转换为Java对象。它使用Jackson库来完成此操作。
  2. 响应处理:当生成响应时,MappingJackson2HttpMessageConverter将Java对象转换为JSON格式的响应内容。它再次使用Jackson库来完成此操作。

MappingJackson2HttpMessageConverter通过实现HttpMessageConverter接口并重写相关方法,完成请求和响应内容的转换。当Spring处理请求或生成响应时,它会自动选择合适的消息转换器,并使用它来处理请求和响应内容。

MappingJackson2HttpMessageConverter如何将请求内容转换为Java对象和响应内容转换为JSON

  • 请求内容到Java对象的转换:
  1. MappingJackson2HttpMessageConverter重写了read()方法,该方法接收请求内容和Java对象类型作为参数。
  2. 该方法使用ObjectMapper类,它是Jackson库的主要组件,将请求内容解析为Java对象。
  3. 解析的Java对象作为方法的返回值,并作为请求的参数传递到控制器方法中。
  • 响应内容到JSON的转换: MappingJackson2HttpMessageConverter
  1. 重写了write()方法,该方法接收Java对象和输出流作为参数。
  2. 该方法使用ObjectMapper类将Java对象序列化为JSON格式的内容。
  3. 序列化的内容写入输出流,并作为响应内容返回给客户端。

简单使用:

@Configuration
public class MvcConfig implements WebMvcConfigurer {
  
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new MappingJackson2HttpMessageConverter(objectMapper()));
    }
    
    @Bean
    public ObjectMapper objectMapper() {
        return new Jackson2ObjectMapperBuilder()
                .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
                .featuresToEnable(SerializationFeature.INDENT_OUTPUT)
                .build();
    }
}

这样,在控制器方法中使用@RequestBody或@ResponseBody注解时,就可以通过MappingJackson2HttpMessageConverter进行序列化/反序列化操作了。

自定义MappingJackson2HttpMessageConverter

将时间戳序列化为LocalDateTime,将LocalDateTime反序列化为时间戳

public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    // 时间对象自定义格式化
    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            long timestamp = Long.parseLong(jsonParser.getText());
            Instant instant = Instant.ofEpochMilli(timestamp);
            return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        }
    });
    javaTimeModule.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeNumber(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        }
    });
    javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
    javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
    // Long转换为String传输
    javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
    mapper.registerModule(javaTimeModule);

    converter.setObjectMapper(mapper);
    return converter;
}

自定义MappingJackson2HttpMessageConverter没有生效?

一般通过从写configureMessageConverters方法,即可将自定义的MappingJackson2HttpMessageConverter添加到Spring的消息转换列表

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        WebMvcConfigurer.super.configureMessageConverters(converters);
        // 时间对象自定义格式化
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 时间对象自定义格式化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                long timestamp = Long.parseLong(jsonParser.getText());
                Instant instant = Instant.ofEpochMilli(timestamp);
                return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
            }
        });
        javaTimeModule.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
            @Override
            public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                jsonGenerator.writeNumber(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
            }
        });
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        // Long转换为String传输
        javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
        mapper.registerModule(javaTimeModule);

        converter.setObjectMapper(mapper);
        converters.add(converter);
    }

image.png 从图中可以看出,converters 数组下标为10的就是我们刚刚添加的消息转换器。 但其实这样是不生效的

具体可以看 SpringAbstractMessageConverterMethodArgumentResolver 类的readWithMessageConverters 方法 image.png 这里大概意思就是遍历消息转换列表找到合适的转换器,只要找到一个合适的就立刻跳出循坏。 途中可以看到有3个MappingJackson2HttpMessageConverter,其中两个怎么来的,大家可以自行搜索一下。 而我们自定义的MappingJackson2HttpMessageConverter放在了集合的最后面,自然不生效了

解决办法一

想要生效,其实只要把自定义的MappingJackson2HttpMessageConverter位置调整一下,如图:

image.png

可以看到自定义的MappingJackson2HttpMessageConverter已经到了第一位,数组下标为0 image.png

解决办法二

@Bean MappingJackson2HttpMessageConverter方式,注意这里是实现WebMvcConfigurer接口而不是继承WebMvcConfigurationSuppor

image.png

具体可以看 Spring 的 JacksonHttpMessageConvertersConfiguration 类, @ConditionalOnMissingBean ,当我们用@Bean创建了MappingJackson2HttpMessageConverter的时候,Spring就不会再去创建它了

image.png

解决办法三

如果是继承WebMvcConfigurationSupport的,要重写extendMessageConverters方法,这里不再展开了

个人见解,如有不对,欢迎在评论提出。感谢!

参考链接: www.cnblogs.com/guoxiaoyu/p…