定制化原理
# 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 ,读者可以通过切换分支来查看本文的示例代码
- 01
- 中国网络防火长城简史 转载10-12
- 03
- 公告:博客近期 RSS 相关问题10-02