HttpMessageConverter 自动配置过程分析

第一步:所有 WebMvcConfigurer 实现类注册到 Spring 容器中

一般来说,容器中至少会会注册 WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter 这个实现类。

如果有 SpringMVC 定制化的需求,那么我们自己实现的 WebMvcConfigurer 也会注册到容器中,从而 Spring 容器中包含多个 WebMvcConfigurer 实现类。

第二步:WebMvcAutoConfiguration$EnableWebMvcConfiguration 自动装配其父类 DelegatingWebMvcConfiguration 属性 configurers

DelegatingWebMvcConfigurationconfigurers 属性是 WebMvcConfigurerComposite 类型,该属性被配置为自动装配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

// 自动装配:Spring 将容器中的所有 WebMvcConfigurer 实现类收集起来,封装为一个 configurers 传入
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}

// ...
}

WebMvcConfigurerComposite 也是 WebMvcConfigurer 的实现类,只不过它没有注册到容器中,它的主要作用是,保存整个 Spring 容器中注册的所有 WebMvcConfigurer 实现类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class WebMvcConfigurerComposite implements WebMvcConfigurer {

// delegates 属性保存 Spring 容器中的所有 WebMvcConfigurer 实现类,初始化为空列表
private final List<WebMvcConfigurer> delegates = new ArrayList<>();

// 参数 configurers 传递过来当前 Spring 容器中的所有 WebMvcConfigurer 实现类
// 该函数在 DelegatingWebMvcConfiguration 的 setConfigurers 方法中调用
public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.delegates.addAll(configurers);
}
}

// ...
}

内部类 EnableWebMvcConfiguration 的父类是 DelegatingWebMvcConfiguration
DelegatingWebMvcConfiguration 的父类是 WebMvcConfigurationSupport

1
2
3
4
5
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {}

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {}

第三步:WebMvcAutoConfiguration$EnableWebMvcConfiguration 注册一些 SpringMVC 组件,比如 RequestMappingHandlerAdapter,注册时需要调用 getMessageConverters() 获取 HttpMessageConverter 并设置到组件的内部属性中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
// 调用父类 WebMvcConfigurationSupport 的 requestMappingHandlerAdapter 来构造 adapter
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
conversionService, validator);
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
}
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
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {

RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
// 获取 HttpMessageConverter 并设置到 adapter 中
adapter.setMessageConverters(getMessageConverters());
// ...
}

protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
// 初始为空,先创建一个空列表
this.messageConverters = new ArrayList<>();
// 遍历容器中所有已注册的 WebMvcConfigurer,依次调用各自 configureMessageConverters 实现,来对 this.messageConverters 进行配置
configureMessageConverters(this.messageConverters);
// 如果遍历所有已注册的 WebMvcConfigurer,仍然没有配置任何 HttpMessageConverter 到 this.messageConverters 中
if (this.messageConverters.isEmpty()) {
// 此时添加一些默认 HttpMessageConverter 到 this.messageConverters 中
addDefaultHttpMessageConverters(this.messageConverters);
}
// 遍历容器中所有已注册的 WebMvcConfigurer,依次调用各自 extendMessageConverters 实现,来对 this.messageConverters 进行配置
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
}

第四步 遍历容器中所有已注册的 WebMvcConfigurer,依次调用各自 configureMessageConverters 实现,来对 this.messageConverters 进行配置

configureMessageConverters(this.messageConverters); 执行动态绑定到 DelegatingWebMvcConfiguration 的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// DelegatingWebMvcConfiguration
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.configureMessageConverters(converters);
}

// WebMvcConfigurerComposite
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 遍历容器中所有已注册的 `WebMvcConfigurer`,依次调用各自 `configureMessageConverters` 实现,来对 `this.messageConverters` 进行配置
for (WebMvcConfigurer delegate : this.delegates) {
delegate.configureMessageConverters(converters);
}
}

WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter 执行 configureMessageConverters

一般来说,WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter 会在 this.delegates 中排在最前面,也即会首先调用其 configureMessageConverters 实现:

1
2
3
4
5
6
// WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.messageConvertersProvider
.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
}

this.messageConvertersProviderObjectProvider<HttpMessageConverters> 类型,它是 WebMvcAutoConfigurationAdapter 构造方法时传入的,用于延迟对从容器中查找和创建 HttpMessageConverters 的操作。

直到在 configureMessageConverters 方法中调用 ifAvailable 方法时,才会尝试从容器中获取一个 HttpMessageConverters 对象。

获取到以后,就将该对象内部保存的所有 HttpMessageConverter 获取到,最后添加到 WebMvcConfigurationSupport#messageConverters 中。

1
2
3
4
5
6
7
8
default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
// 从容器中获取 HttpMessageConverters
T dependency = getIfAvailable();
if (dependency != null) {
// 如果获取到,调用传入的 lambda 表达式,将 HttpMessageConverters 内部的 HttpMessageConverter 都添加到 WebMvcConfigurationSupport#messageConverters 中
dependencyConsumer.accept(dependency);
}
}

HttpMessageConverters 是何时注册到容器中的呢?是 HttpMessageConvertersAutoConfiguration 这个自动配置类注册进去的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpMessageConverter.class)
@Conditional(NotReactiveWebApplicationCondition.class)
@AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class, JsonbAutoConfiguration.class })
@Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class,
JsonbHttpMessageConvertersConfiguration.class })
public class HttpMessageConvertersAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}
}

能看到,这里也使用了 ObjectProvider,只不过延迟查找的类型是 HttpMessageConverter 而不是 HttpMessageConverters

方法内部就是简单地调用了 HttpMessageConverters 的有参构造器,唯一的构造参数是通过 ObjectProvider 查找出此时 Spring 容器中所有的 HttpMessageConverter 并封装为 List 集合传入。

回顾之前在 SpringMVC 中说过的,SpringBoot 会自动配置两个 StringHttpMessageConverter,其中默认编码为 UTF-8StringHttpMessageConverter 就是在这里先从容器中获取到,然后作为构造参数传递给 HttpMessageConverters 的构造方法的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>> {
public HttpMessageConverters(Collection<HttpMessageConverter<?>> additionalConverters) {
this(true, additionalConverters);
}

public HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters) {
// 将从容器中查找到的所有 HttpMessageConverter 与默认的 HttpMessageConverters 合并保存到 HttpMessageConverters 内部,即保存到 this.converters 属性
List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
addDefaultConverters ? getDefaultConverters() : Collections.emptyList());
combined = postProcessConverters(combined);
this.converters = Collections.unmodifiableList(combined);
}


}

我们来看一下 getDefaultConverters() 获取到哪些转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private List<HttpMessageConverter<?>> getDefaultConverters() {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
// 如果 WebMvcConfigurationSupport 类存在,就调用该类中的 getMessageConverters 来获取默认转换器
if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport",
null)) {
converters.addAll(new WebMvcConfigurationSupport() {

public List<HttpMessageConverter<?>> defaultMessageConverters() {
return super.getMessageConverters();
}

}.defaultMessageConverters());
}
else {
converters.addAll(new RestTemplate().getMessageConverters());
}
reorderXmlConvertersToEnd(converters);
return converters;
}

该方法主要是检查 WebMvcConfigurationSupport 类是否存在,如果存在,就调用该类中的成员方法 getMessageConverters() 获取默认转换器。

这里写法奇怪的原因是,WebMvcConfigurationSupport#getMessageConverters 是个成员方法,因此创建了一个匿名内部类对象,再调用 super.getMessageConverters 来实现调用。

1
2
3
4
5
6
7
8
9
10
11
12
// WebMvcConfigurationSupport
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;
}

很眼熟,兜兜转换,我们又回到了 getMessageConverters 方法。但要注意,该方法是一个成员方法!

之前,是 WebMvcAutoConfiguration$EnableWebMvcConfiguration 调用的,目的是为注册 RequestMappingHandlerAdapter 组件时,设置适配器组件内部的转换器属性。

这里,是一个 WebMvcConfigurationSupport 的匿名内部类调用的,该匿名内部类没有重写任何方法,因此调用 getMessageConverters 方法时,方法内部的 configureMessageConvertersextendMessageConverters,均保持为 WebMvcConfigurationSupport 的空实现版本,什么都不干。

也就是说,只有 addDefaultHttpMessageConverters(this.messageConverters); 会起作用,addDefaultHttpMessageConvertersWebMvcConfigurationSupport 有实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new ResourceRegionHttpMessageConverter());
try {
messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Throwable ex) {
// Ignore when no TransformerFactory implementation is available...
}
messageConverters.add(new AllEncompassingFormHttpMessageConverter());

if (jackson2Present) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}

WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter 执行 configureMessageConverters 方法的整个过程分析完毕,小结一下。

其实整个过程就是从容器中获取 HttpMessageConverters 对象,然后取出该对象内部保存的所有 HttpMessageConverter 添加到 WebMvcAutoConfiguration$EnableWebMvcConfigurationmessageConverters 属性中去。

HttpMessageConverters 内部的 HttpMessageConverter 来自两个地方:一是 Spring 容器中已注册的,二是 WebMvcConfigurationSupport 类的 addDefaultHttpMessageConverters 设置的默认转换器。

既然 HttpMessageConverters 内部的 HttpMessageConverter 可以来自 Spring 容器中已注册的,那么我们想要自定义 HttpMessageConverter,其实也可以将我们的自定义 HttpMessageConverter 直接注册到 Spring 容器中!

自定义 WebMvcConfigurer 实现类执行 configureMessageConverters 方法

1
2
3
4
5
6
7
8
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyHttpMessageConverter());
}
}

注意,这里传入的 converters 参数,就已经包含了 WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter 在执行 configureMessageConverters 时添加进去的一些转换器。

第五步:如果遍历所有已注册的 WebMvcConfigurer 执行 configureMessageConverters,仍然没有配置任何 HttpMessageConverter 到 this.messageConverters 中,此时添加默认 HttpMessageConverter 到 this.messageConverters 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
// 初始为空,先创建一个空列表
this.messageConverters = new ArrayList<>();
// 遍历容器中所有已注册的 WebMvcConfigurer,依次调用各自 configureMessageConverters 实现,来对 this.messageConverters 进行配置
configureMessageConverters(this.messageConverters);
// 如果遍历所有已注册的 WebMvcConfigurer,仍然没有配置任何 HttpMessageConverter 到 this.messageConverters 中
if (this.messageConverters.isEmpty()) {
// 此时添加一些默认 HttpMessageConverter 到 this.messageConverters 中
addDefaultHttpMessageConverters(this.messageConverters);
}
// 遍历容器中所有已注册的 WebMvcConfigurer,依次调用各自 extendMessageConverters 实现,来对 this.messageConverters 进行配置
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}

一般来说,第五步不会执行。

第六步:遍历容器中所有已注册的 WebMvcConfigurer,依次调用各自 extendMessageConverters 实现,来对 this.messageConverters 进行配置

同样地,一般来说,WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter 会在 this.delegates 中排在最前面,也即会首先调用其 extendMessageConverters 实现。

不过 WebMvcAutoConfigurationAdapter 没有实现 extendMessageConverters 方法,因此保持为 WebMvcConfigurer 的默认接口空实现。

然后是,自定义 WebMvcConfigurer 实现类执行 extendMessageConverters 方法来添加转换器配置。

疑问:configureMessageConvertersextendMessageConverters 的区别

这里我有一个疑问是,configureMessageConverters 似乎和 extendMessageConverters 的功能是有些重合的,都能做 HttpMessageConverter 的配置,为什么 WebMvcConfigurer 接口中要同时提供这两个接口方法呢?

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
public interface WebMvcConfigurer {

/**
* Configure the {@link HttpMessageConverter HttpMessageConverters} to use for reading or writing
* to the body of the request or response. If no converters are added, a
* default list of converters is registered.
* <p><strong>Note</strong> that adding converters to the list, turns off
* default converter registration. To simply add a converter without impacting
* default registration, consider using the method
* {@link #extendMessageConverters(java.util.List)} instead.
* @param converters initially an empty list of converters
*/
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}

/**
* A hook for extending or modifying the list of converters after it has been
* configured. This may be useful for example to allow default converters to
* be registered and then insert a custom converter through this method.
* @param converters the list of configured converters to extend.
* @since 4.1.3
*/
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
}

官方建议我们使用 extendMessageConverters 来添加自定义转换器或者修改已配置的转换器;若使用 configureMessageConverters 来添加转换器,会关闭默认转换器的注册工作,也就是关闭 addDefaultHttpMessageConverters 的执行。

前半句可以记下来,但是后半句感觉有问题。

我们再回过头来看 getMessageConverters() 的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
// 初始为空,先创建一个空列表
this.messageConverters = new ArrayList<>();
// 遍历容器中所有已注册的 WebMvcConfigurer,依次调用各自 configureMessageConverters 实现,来对 this.messageConverters 进行配置
configureMessageConverters(this.messageConverters);
// 如果遍历所有已注册的 WebMvcConfigurer,仍然没有配置任何 HttpMessageConverter 到 this.messageConverters 中
if (this.messageConverters.isEmpty()) {
// 此时添加一些默认 HttpMessageConverter 到 this.messageConverters 中
addDefaultHttpMessageConverters(this.messageConverters);
}
// 遍历容器中所有已注册的 WebMvcConfigurer,依次调用各自 extendMessageConverters 实现,来对 this.messageConverters 进行配置
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}

我们自定义 WebMvcConfigurer 实现类,并调用 configureMessageConverters 添加转换器,确实会导致 this.messageConverters 不为空,从而 addDefaultHttpMessageConverters 不执行。

但问题是,WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapte 本身也向 this.messageConverters 中添加了一堆转换器(其中就包括默认的转换器),也就是说,就算我们不做定制化,addDefaultHttpMessageConverters 一样是不会执行的。

我猜测,官方说的“使用 configureMessageConverters 来添加转换器会关闭默认转换器的注册工作,也就是关闭 addDefaultHttpMessageConverters 的执行”,应该指的是我们全面接管 SpringMVC 的定制化场景,而不是我们现在的通过 WebMvcConfigurer 进行 SpringMVC 定制化的场景。