springboot如何自动配置MappingJackson2HttpMessageConverter

Posted by hcy on February 15, 2020

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/