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); if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } 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 { 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 { 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
| @Override public void afterPropertiesSet() { 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); } 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
| private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
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));
handlers.add(new ModelAttributeMethodProcessor(false)); handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor());
if (getCustomReturnValueHandlers() != null) { handlers.addAll(getCustomReturnValueHandlers()); }
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()); 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<>(); 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) { 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 {
@Bean @Primary @ConditionalOnMissingBean public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { return builder.createXmlMapper(false).build(); }
}
@Bean @ConditionalOnMissingBean public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder( List<Jackson2ObjectMapperBuilderCustomizer> customizers) { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.applicationContext(this.applicationContext); customize(builder, customizers); return builder; }
@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
帮我们创建的,但不生效?
如何做才是最佳实践?
下篇讲讲上面三个问题。