简介:
MappingJackson2HttpMessageConverter是一个Spring消息转换器,用于在Web应用程序中处理请求和响应内容。它的工作原理如下:
- 请求处理:当接收到请求时,MappingJackson2HttpMessageConverter将请求内容(通常是JSON格式)转换为Java对象。它使用Jackson库来完成此操作。
- 响应处理:当生成响应时,MappingJackson2HttpMessageConverter将Java对象转换为JSON格式的响应内容。它再次使用Jackson库来完成此操作。
MappingJackson2HttpMessageConverter通过实现HttpMessageConverter接口并重写相关方法,完成请求和响应内容的转换。当Spring处理请求或生成响应时,它会自动选择合适的消息转换器,并使用它来处理请求和响应内容。
MappingJackson2HttpMessageConverter如何将请求内容转换为Java对象和响应内容转换为JSON
- 请求内容到Java对象的转换:
- MappingJackson2HttpMessageConverter重写了read()方法,该方法接收请求内容和Java对象类型作为参数。
- 该方法使用ObjectMapper类,它是Jackson库的主要组件,将请求内容解析为Java对象。
- 解析的Java对象作为方法的返回值,并作为请求的参数传递到控制器方法中。
- 响应内容到JSON的转换: MappingJackson2HttpMessageConverter
- 重写了write()方法,该方法接收Java对象和输出流作为参数。
- 该方法使用ObjectMapper类将Java对象序列化为JSON格式的内容。
- 序列化的内容写入输出流,并作为响应内容返回给客户端。
简单使用:
@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);
}
从图中可以看出,converters 数组下标为10的就是我们刚刚添加的消息转换器。 但其实这样是不生效的
具体可以看 Spring 的 AbstractMessageConverterMethodArgumentResolver 类的readWithMessageConverters 方法 这里大概意思就是遍历消息转换列表找到合适的转换器,只要找到一个合适的就立刻跳出循坏。 途中可以看到有3个MappingJackson2HttpMessageConverter,其中两个怎么来的,大家可以自行搜索一下。 而我们自定义的MappingJackson2HttpMessageConverter放在了集合的最后面,自然不生效了
解决办法一
想要生效,其实只要把自定义的MappingJackson2HttpMessageConverter位置调整一下,如图:
可以看到自定义的MappingJackson2HttpMessageConverter已经到了第一位,数组下标为0
解决办法二
@Bean MappingJackson2HttpMessageConverter方式,注意这里是实现WebMvcConfigurer接口而不是继承WebMvcConfigurationSuppor
具体可以看 Spring 的 JacksonHttpMessageConvertersConfiguration 类, @ConditionalOnMissingBean ,当我们用@Bean创建了MappingJackson2HttpMessageConverter的时候,Spring就不会再去创建它了
解决办法三
如果是继承WebMvcConfigurationSupport的,要重写extendMessageConverters方法,这里不再展开了
个人见解,如有不对,欢迎在评论提出。感谢!