浅析 Spring 和 SpringBoot 对 web MVC 配置的区别及其原理
本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
众所周知,Spring 和 SpringBoot 对 web MVC 的推荐配置方式是有所不同的
其实,对大多数模块或者三应用来说,与 Spring 的集成通常是这样一种模式:
Spring基于模块的功能或者三方应用的API提供对应的bean组件- 由使用者(我们)来根据具体的场景注册对应的
bean
而 SpringBoot 是这样的:
- 提供了强大的自动装配,可以理解为提供了一套理论上的最佳实践
- 同时更可贵也必须的是允许使用者的自定义覆盖
- 这也正是
SpringBoot更加流行的原因之一(约定大于配置)
对 web MVC 的配置来说,也可以理解为这种模式,但也有所不同,本文简单的聊一下个人对这些内容的理解
Spring
EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
EnableXXX是Spring常用的模块化设计模式- 引入了配置类
DelegatingWebMvcConfiguration
DelegatingWebMvcConfiguration
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
// 收集容器中所有的 WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
// 重写 WebMvcConfigurationSupport 的部分方法,把它们的实现委托到 List<WebMvcConfigurer> configurers 上
@Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
this.configurers.configurePathMatch(configurer);
}
// ...
}
- 首先它是一个
WebMvcConfigurationSupport,WebMvcConfigurationSupport是提供Spring MVC核心组件的配置类,它允许子类在它的基础上解析自定义拓展,而这个任务就由DelegatingWebMvcConfiguration完成 DelegatingWebMvcConfiguration用WebMvcConfigurerComposite来收集容器中所有的WebMvcConfigurer,转而把对WebMvcConfigurationSupport组件方法的拓展委托给这些WebMvcConfigurer,比如方法configurePathMatch
WebMvcConfigurer
public interface WebMvcConfigurer {
/**
* 自定义配置 PathMatchConfigurer
*/
default void configurePathMatch(PathMatchConfigurer configurer) {
}
// ...
}
- 因此,我们覆盖、拓展
Spring默认提供的MVC组件,实现该接口并复写对应方法即可 - 一般我们使用主配置类实现该接口,当然也可以提供多个
WebMvcConfigurer - 当然,它的拓展是有限的,毕竟依托于
WebMvcConfigurationSupport暴露出来的方法,因此更加全面细粒度的控制则可以直接继承WebMvcConfigurationSupport,但这一般没有必要
小结
其实我理解这类似于一种 Spring 自己的自动装配机制:提供一组最佳实践的组件并允许使用者覆盖,覆盖模式就是复写暴露出来的对应方法
Spring Boot
Spring Boot 使用的是经典的自动装配模式
WebMvcAutoConfiguration
@Configuration(proxyBeanMethods = false)
// ...
public class WebMvcAutoConfiguration {
// ...
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
// ...
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
// ...
}
// ...
}
- 该装配类的整体结构如上,其中
WebMvcAutoConfigurationAdapter,该配置类是一个WebMvcConfigurer,相当于Spring Boot自己提供了一个WebMvcConfigurer,同时import了一个EnableWebMvcConfigurationEnableWebMvcConfiguration是一个DelegatingWebMvcConfiguration,相当于Spring Boot帮我们完成了在Spring中相当于@EnableWebMvc的工作并进行了拓展
小结
Spring Boot相当于自己同时提供了DelegatingWebMvcConfiguration和WebMvcConfigurer,这也正符合Spring Boot的理念- 对应可配置的属性则是通过
ConfigurationProperties暴露出去
总结
因此,我们在基于 Spring Boot 使用 web MVC 时一般不建议使用 @EnableWebMvc,因为这相当于引入了 Spring 提供的 DelegatingWebMvcConfiguration,两者的组件必然会发生优先级的冲突
具体组件冲突取决于 @ConditionalOnXXX @Primary 等注解,就不
一一解析了