springboot配置MappingJackson2HttpMessageConverter最佳实践

上篇讲了,SpringMvc是如何将返回值转成Json的

默认情况下Springboot是如何配置HandlerAdapter的,如何根据配置文件影响到ObjectMapper的创建的。

下面讲一讲如何做才是最佳实践,如何做能满足需求。

1. 最大限度使用Springboot的自动配置

如果我们想最大限度使用Springboot,且想修改接口返回Json格式等,那么我们可以在配置文件中配置常用的配置。

我们在配置文件里打出spring.jacksonide会给我们提示,我们发现常用的配置都有,比如配置输出时间格式的

1
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss

也可以打开看看下面这个类,里面也有对应的配置

1
org.springframework.boot.autoconfigure.jackson.JacksonProperties

2.使用自己创建的ObjectMapper

创建一个ObjectMapperBean,但要标记为@Primary才能覆盖自动配置的。这样的话配置文件里的配置不会生效,我们自己创建的就可以随心所欲的配置了。

并且推荐使用下面的第二种方式创建,点开源码能看到它里面添加了额外的配置。

1
2
3
4
5
6
7
@Bean
@Primary
public ObjectMapper objectMapper (){
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
return objectMapper;
}
1
2
3
4
5
6
7
8
@Bean
@Primary
public ObjectMapper objectMapper (){
return Jackson2ObjectMapperBuilder
.json()
.dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
.build();
}

因为org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration里的自动配置,加入了@ConditionalOnMissingBean注解,所以系统就不会自动配置了。

3. 使用自己创建的MappingJackson2HttpMessageConverter

1
2
3
4
5
6
@Bean
public MappingJackson2HttpMessageConverter objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
return new MappingJackson2HttpMessageConverter(objectMapper);
}

因为org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration类中对MappingJackson2HttpMessageConverter的自动配置添加了条件判断,所以自动配置就不会执行了。

判断如下

1
@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class,

4.如果继承WebMvcConfigurationSupport

这个类里面又很多MVC相关的配置,我个人比较习惯继承这个类,但是继承这个类后,上面三种方法无论是改配置文件,自定义ObjectMapper,还是自己定义MappingJackson2HttpMessageConverter均失效了。

大家可以试一下,那么是什么原因导致失效的呢?,如何解决这个问题?

1.是什么原因导致失效的呢?

我们可以看一下WebMvcAutoConfiguration的源码,发现这个类上有@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)的注解,

也就是说找不到WebMvcConfigurationSupport才进行自动配置,而我们继承了WebMvcConfigurationSupport,并添加@Configuration,则MapperAdaper不会被自动配置,所以自动创建的

MappingJackson2HttpMessageConverterObjectMapper都虽然创建了但也不会起作用了。

2. 如何解决这个问题?

既然我习惯继承WebMvcConfigurationSupport,如果想定制ObjectMapper该怎么办呢,查看WebMvcConfigurationSupport源码,发现他有这样一个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(mvcContentNegotiationManager());
//这里注入了MessageConverter,请进入getMessageConverters()方法
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

if (jackson2Present) {
adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
}

AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
configureAsyncSupport(configurer);
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

return adapter;
}

从上面源码能看到,没有在WebMvcAutoConfiguration里面配置的RequestMappingHandlerAdapter在这里配置了,但这里并没有使用Spring容器里面的ObjectMapperMappingJackson2HttpMessageConverter,而是新创建的,所以系统自动配置的和我们自己注入到Spring容器的才会失效,因为根本就没有被使用。

我们看一下是如何添加MessageConverter的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<>();
//这个方法是空实现
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
//添加默认的messageConverters
addDefaultHttpMessageConverters(this.messageConverters);
}
//继续处理
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}

上面代码能看出,首先是通过configureMessageConverters(this.messageConverters)方法配置,但该方法是空实现,然后添加默认的MessageConverter,然后extendMessageConverters(this.messageConverters)

所以我们可以在继承类中重写extendMessageConverters方法,在这个类中对默认的MessageConverter进行修改或添加删除等操作,如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//将StringHttpMessageConverter设成UTF8格式
converters.stream()
.filter(c -> c instanceof StringHttpMessageConverter)
.map(c -> (StringHttpMessageConverter) c)
.forEach(c -> c.setDefaultCharset(StandardCharsets.UTF_8));

//将MappingJackson2HttpMessageConverter设为自己的ObjectMapper
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converters.stream()
.filter(c -> c instanceof MappingJackson2HttpMessageConverter)
.map(c -> (MappingJackson2HttpMessageConverter) c)
.forEach(c -> c.setObjectMapper(mapper));
}

5.总结

如果你不打算继承WebMvcConfigurationSupport,那么自动配置生效,你可以通过配置文件配置Jackson或通过提供自己的类配置Jackson,但注意如果覆盖ObjectMapper要加@Primary注解才行。

如果你不小心或打算继承WebMvcConfigurationSupport,那上面的自动配置依然会执行,但不会被使用,这就导致上面的配置不灵了,可以实现extendMessageConverters方法,在里面我们可以任意处理MessageConverter

6.回答下上篇文章的问题

  1. 配置文件里配置列Jackson但不生效?

可能是覆盖了WebMvcConfigurationSupport,或重写了ObjectMapper或重写了MappingJackson2HttpMessageConverter,这样导致配置文件里的不生效了。

  1. 自己手动创建ObjectMapper来代替Springboot帮我们创建的,但不生效?

你肯定是继承了WebMvcConfigurationSupport

  1. 如何做才是最佳实践?

上面已经列举了四种方式,要注意他们的优先级,且继承WebMvcConfigurationSupport会导致前三种方式创建的对象不被使用,也就是失效了。


springboot配置MappingJackson2HttpMessageConverter最佳实践
https://www.huangchaoyu.com/643050368.html
作者
hcy
发布于
2020年2月16日
更新于
2024年8月17日
许可协议