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
帮我们创建的,但不生效?
如何做才是最佳实践?
下篇讲讲上面三个问题。
转载请注明出处:https://www.huangchaoyu.com/2020/02/15/springboot如何自动配置MappingJackson2HttpMessageConverter/