springboot如何自动配置MappingJackson2HttpMessageConverter

springboot开箱即用,写一个Controller,再配合RestController,无需其他配置,就能直接返回Json到前台,那他是怎么做到的呢?,如何定制它?,因为只靠默认是不能满足我们的需求的,花了点时间看下源码,进行如下总结。

springmvc如何将返回值转成json的

我们知道,当请求到达DispatchServlet时,该servlet会查找到对应的HandlerMapper,然后查找与之适配的HandlerAdapter。使用HandlerAdapter.handler(request,response)来处理请求。

下面看HandlerAdapter里的invokeHandlerMethod方法是如何处理请求的

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandler#invokeHandlerMethod

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//设置用来解析参数的argumentResolvers到invocableMethod
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
//设置用来处理返回值的returnValueHandlers到invocableMethod
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//真实调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}

return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

下面看invocableMethod真实调用时的源码:

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
29
30
31
32
33
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//调用我们写的controller,获取返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);

if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}

mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
//使用returnValueHandlers处理返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}

上面可以看到,HandlerAdapter会调用我们字节写的controller方法,controller返回结果使用returnValueHandlers来处理的。

下面我看带着疑问,什么是returnValueHandlers?他是什么时候被创建的?他是如何处理返回值的?

查看handlerAdapet的源码发现有个这个方法,用于初始化多个returnValueHandlers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//此方法会当属性设置完毕后被spring回掉
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();

if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//这个方法初始化ReturnValueHandlers
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//可以看到此方法初始化了多个HandlerMethodReturnValueHandler
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

// Annotation-based return value types
handlers.add(new ModelAttributeMethodProcessor(false));

//转json真正使用的handlers,参数传入了adapter内的所有MessageConverters
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));

// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());

// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}

// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ModelAttributeMethodProcessor(true));
}

return handlers;
}

看上面的源码,回答下上面的疑问

什么是returnValueHandlers?

他是用于处理controller的返回值的处理器,它都多个实现类存在于MapperAdapter里,不同的returnValueHandlers能处理不同类型的返回值,责任链模式调用。

他是什么时候被创建的?

他在afterPropertiesSet方法里被初始化,这个方法是InitializingBean接口的方法,从名字可以看出这个方法在类属性设置完成后被Spring回掉,所以在这里创建了多个returnValueHandlers存在在list里,我们要注意的是,其中有一个RequestResponseBodyMethodProcessor,创建它时将HandlerAdapter里面的所有MessageConverter都传进去了。

他是如何处理返回值的?

他是使用构造参数传入的MessageConverter处理的,其中有一个MessageConverter就是MappingJackson2HttpMessageConverter,这个类很熟悉,就是他将返回值转成Json的。

看看MappingJackson2HttpMessageConverter什么时候设置进HandlerAdapter里面的

既然知道返回值是何时被转成Json的,又有了新疑问,那就是HandlerAdapter里面的MessageConverter是什么时候被设置的?,以及MappingJackson2HttpMessageConverter是什么时候被设置的?

我们找到了WebMvcAutoConfiguration这个类,这里我们看到了自动初始化RequestMappingHandlerAdapter的方法,进入方法内部查看,我们找到了设置HttpMessageConverter的过程。

创建RequestMappingHandlerAdapter的方法

1
2
3
4
5
6
7
8
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}

查看super.requestMappingHandlerAdapter(),我们找到下面的方法

1
2
3
4
5
6
7
8
9
10
11
12
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(mvcContentNegotiationManager());
//创建后,设置了MessageConverters
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
.
.
.
1
2
3
4
5
6
7
8
9
10
11
12
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<>();
//配置MessageConverters的方法
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}

configureMessageConverters(this.messageConverters)方法被父类重写了

org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration#configureMessageConverters

1
2
3
4
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.configureMessageConverters(converters);
}

上面方法中啥是configurers?,什么时候被创建的?

我们发现configurers是通过创建DelegatingWebMvcConfiguration时,通过构造方法注入的List<WebMvcConfigurer> configurers创建的。

我们在WebMvcAutoConfiguration里面发现WebMvcAutoConfigurationAdapter类继承了WebMvcConfigurer,且会被自动注入到Spring内的,所以删改你创建DelegatingWebMvcConfiguration时使用的WebMvcConfigurer就是此类,此类的configureMessageConverters方法会对HttpMessageConverter进行配置。看一下它的源码。

WebMvcAutoConfiguration#WebMvcAutoConfigurationAdapter类

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


public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
}


@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.messageConvertersProvider.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
}

configureMessageConverters方法通过messageConvertersProvider来设置MessageConverter到list中,messageConvertersProvider是通过构造方法注入进来的,我们要找找messageConvertersProvider是什么时候注入Spring的?

顺着泛型信息,我们找到了HttpMessageConvertersAutoConfiguration,他也是自动配置的类,它提供了HttpMessageConverters这个Bean。

org.springframework.boot.autoconfigure.http.HttpMessageConverters

1
2
3
4
5
6
7
8
9
10
11
12
	
private final List<HttpMessageConverter<?>> converters;

public HttpMessageConvertersAutoConfiguration(ObjectProvider<HttpMessageConverter<?>> convertersProvider) {
this.converters = convertersProvider.orderedStream().collect(Collectors.toList());
}

@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters() {
return new HttpMessageConverters(this.converters);
}

HttpMessageConverters的构造方法里面是这样处理的

1
2
3
4
5
6
public HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters) {
//注意这里的getDefaultConverters()方法
List<HttpMessageConverter<?>> combined = getCombinedConverters(converters, addDefaultConverters ? getDefaultConverters() : Collections.emptyList());
combined = postProcessConverters(combined);
this.converters = Collections.unmodifiableList(combined);
}

HttpMessageConvertersAutoConfiguration类里面可以看出,他本身会在构造方法里通过getDefaultConverters()方法创建默认的MessageConverter,其次他还会通过HttpMessageConvertersAutoConfiguration的构造方法注入外界的HttpMessageConverter,合并在一起。

下面看看构造方法里的ObjectProvider<HttpMessageConverter<?>> convertersProvider里面从哪里来的?

我们发现两个地方创建了MessageConverter

一个是org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration.StringHttpMessageConverterConfiguration里面创建的StringHttpMessageConverter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

@Configuration
@ConditionalOnClass(StringHttpMessageConverter.class)
@EnableConfigurationProperties(HttpProperties.class)
protected static class StringHttpMessageConverterConfiguration {

private final HttpProperties.Encoding properties;

protected StringHttpMessageConverterConfiguration(HttpProperties httpProperties) {
this.properties = httpProperties.getEncoding();
}

@Bean
@ConditionalOnMissingBean
public StringHttpMessageConverter stringHttpMessageConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(this.properties.getCharset());
converter.setWriteAcceptCharset(false);
return converter;
}

}

另一个是org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration创建的MappingJackson2HttpMessageConverter

1
2
3
4
5
6
7
8
9

@Bean
@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class,
ignoredType = { "org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
"org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
public mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
return new MappingJackson2HttpMessageConverter(objectMapper);
}

这就解释了为什么HandlerAdapter里面会有MappingJackson2HttpMessageConverter了。

原来是创建HandlerAdapter时的getHandlerMessages方法做了这些工作。

定制MappingJackson2HttpMessageConverter

上面解释了HandlerAdapter里面的HandlerMessage是怎么来的。

但又有一个疑问,问什么我们在Springboot的配置文件里能配置JacksonMessageConverter工作的?

我们知道MappingJackson2HttpMessageConverter处理的核心功能是由ObjectMapper提供的,所有配置都在ObjectMapper里,所以创建MappingJackson2HttpMessageConverter时传入的ObjectMapper是经过Springboot创建并根据配置文件配置过的。

疑问是ObjectMapper是什么时候创建的?如何被配置的?

我又找到了这个类JacksonAutoConfiguration,也是一个自动配置类

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
29
30
31
32
33
34
@Configuration
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperConfiguration {

//通过Jackson2ObjectMapperBuilder创建ObjectMapper
@Bean
@Primary
@ConditionalOnMissingBean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
return builder.createXmlMapper(false).build();
}

}

//通过Jackson2ObjectMapperBuilderCustomizer创建Jackson2ObjectMapperBuilder
@Bean
@ConditionalOnMissingBean
public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder(
List<Jackson2ObjectMapperBuilderCustomizer> customizers) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.applicationContext(this.applicationContext);
customize(builder, customizers);
return builder;
}
/*
创建StandardJackson2ObjectMapperBuilderCustomizer的过程,使用了JacksonProperties
而JacksonProperties就对应了Springboot的配置文件里面的“spring.jackson”开始的配置
*/
@Bean
public StandardJackson2ObjectMapperBuilderCustomizer standardJacksonObjectMapperBuilderCustomizer(
ApplicationContext applicationContext, JacksonProperties jacksonProperties) {
return new StandardJackson2ObjectMapperBuilderCustomizer(applicationContext, jacksonProperties);
}

上面ObjectMapper的创建过程也就能看出为什么application.yml里的配置能影响到ObjectMapper的行为了。

反向总结一下

application.yml中的jackson配置,

导致JacksonProperties里面的属性变化,

导致JacksonAutoConfiguration里面的StandardJackson2ObjectMapperBuilderCustomizer变化,

导致JacksonAutoConfiguration里面的Jackson2ObjectMapperBuilder变化,

导致创建的JacksonAutoConfiguration里面的ObjectMapper属性变化,然后ObjectMapper被注入Spring

然后JacksonHttpMessageConvertersConfiguration里面创建MappingJackson2HttpMessageConverter 时从Spring获取ObjectMapper

HttpMessageConvertersAutoConfiguration里面创建HttpMessageConverters时从Spring获取Jakson2HttmMessageConverter

WebMvcAutoConfiguration创建RequestMappingHandlerMapperAdapter 时从容器获取了HttpMessageConverters,然后设置到RequestResponseBodyMethodProcessor里面。

然后RequestResponseBodyMethodProcessor使用Jakson2HttmMessageConverter 处理Controller的返回值,也就实现了根据配置文件来定制返回JJson的行为了。

但大家一定也遇到入:

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

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

​ 如何做才是最佳实践?

下篇讲讲上面三个问题。


springboot如何自动配置MappingJackson2HttpMessageConverter
https://www.huangchaoyu.com/3222672442.html
作者
hcy
发布于
2020年2月15日
更新于
2024年8月17日
许可协议