定制化原理
# 470.定制化原理
这一小节主要对Web开发,做个总结
# 几种定制化方式
首先,我们的SpringBoot自动配置了很多东西,就算没做任何配置,也能运作良好;
自动配置的过程: 引入场景starter --> 通过xxxxAutoConfiguration自动配置 --> 自动导入xxx组件 -->绑定配置xxxProperties --> 绑定配置文件里的内容
所以,一般要修改什么配置,只需增加一个配置文件即可。除此之外,还有其他方式:
- 编写自定义的配置类(通过
@Configuration
注解),然后注入Bean进行组件注册,例如之前自定义过异常解析器CustomerHandlerExceptionResolver
- 如果是web项目,可以通过实现
WebMvcConfigurer
接口,完成定制化 - 定制化器
xxxxCustomizer
,后续我们会看到很多这样的设计
在文档中也有这样的描述:
4.7.1. The “Spring Web MVC Framework”
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own
@Configuration
class of typeWebMvcConfigurer
but without@EnableWebMvc
.
If you want to provide custom instances of
RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
, orExceptionHandlerExceptionResolver
, and still keep the Spring Boot MVC customizations, you can declare a bean of typeWebMvcRegistrations
and use it to provide custom instances of those components.
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with
@EnableWebMvc
, or alternatively add your own@Configuration
-annotatedDelegatingWebMvcConfiguration
as described in the Javadoc of@EnableWebMvc
大意:
- 如果想要自定义配置,只需写个配置类,实现
WebMvcConfigurer
接口(但是不实现@EnableWebMvc
) - 如果想要修改一些比较底层的配置,但是不改动SpringMVC的默认配置,例如增加
RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
,也是可以的,只需注入一个组件WebMvcRegistrations
即可 - 如果想要完全接管SpringMVC,可以写个配置类,然后使用注解
@EnableWebMvc
第二种方式示例:
@Bean
public WebMvcRegistrations webMvcRegistrations(){
return new WebMvcRegistrations(){
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return null;
}
};
}
2
3
4
5
6
7
8
9
注意,第二种方式是很底层的,如果不是很了解HandlerMapping
,不推荐使用。接下来讲讲@EnableWebMvc
# @EnableWebMvc
在一个配置类使用该注解后,将全面接管SpringMVC的默认行为;想之前的静态资源,视图解析器,内容协商起,Converter,欢迎页.....全部都会失效,因此要慎用!接下来我们讲讲原理。
为什么会失效呢?我们可以看看@EnableWebMvc
的源码,其有引入一个类DelegatingWebMvcConfiguration
:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
2
3
4
5
6
DelegatingWebMvcConfiguration
,继承自WebMvcConfigurationSupport
:
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
2
而自动配置类WebMvcAutoConfiguration
,要生效有一个条件:容器中没有WebMvcConfigurationSupport
,因此我们使用了@EnableWebMvc
注解后,自动配置就失效了
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
public class WebMvcAutoConfiguration {
2
因此,之前的配置都需要我们手工配置,例如设置静态资源:
@EnableWebMvc
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/**
* 访问 /aa/** 所有请求都去 classpath:/static/ 下面进行匹配
*/
registry.addResourceHandler("/aa/**")
.addResourceLocations("classpath:/static/");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
DelegatingWebMvcConfiguration
,也定义一些默认行为,只保证SpringMVC最基本功能的使用。
除此之外,它会获取到所有的Configurer
,然后配置:
//................
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
@Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
this.configurers.configureContentNegotiation(configurer);
}
@Override
protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
this.configurers.configureAsyncSupport(configurer);
}
@Override
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
this.configurers.configureDefaultServletHandling(configurer);
}
@Override
protected void addFormatters(FormatterRegistry registry) {
this.configurers.addFormatters(registry);
}
//................
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
还记得第一种配置方式吗?写个配置类,实现WebMvcConfigurer
接口。因此DelegatingWebMvcConfiguration
的作用,就是将系统中所有的WebMvcConfigurer
,然后循环遍历,使其生效
# 总结
一般来说,我们都是改配置文件,最多实现个WebMvcConfigurer
接口来完成配置,很少完全接管SpringMVC的配置。
通过web开发的源码分析,相信大家对于SpringBoot底层也有了一定的了解,后续学习数据访问、单元测试等,都是事半功倍
已将本文源码上传到Gitee (opens new window)或GitHub (opens new window) 的分支demo8 ,读者可以通过切换分支来查看本文的示例代码