正文内容
前言
在 Spring Boot 开发中,我们经常需要自定义 Web 配置,比如添加拦截器、配置跨域或静态资源映射。我们通常熟知的"标准姿势"是这样的:
java
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加一个拦截器
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
写完这段代码,Spring Boot 启动后,我们的拦截器就神奇地生效了。但你有没有想过:为什么?
- 是谁在调用这个
addInterceptors方法? - 为什么加上
@Configuration它就被发现了? - 如果有多个类都实现了这个接口,Spring 是怎么处理的?
今天我们就来扒一扒这背后的底层原理。
一、 核心三步曲
整个生效流程可以概括为三个核心步骤:Bean 的注册 -> Bean 的搜集 -> Bean 的执行。
这背后运用了 Spring 容器的 集合注入(Collection Injection) 特性和 委派模式(Delegation Pattern)。
1. 入场券:@Configuration
首先,我们在类上加了 @Configuration 注解。
这意味着该类被 Spring 扫描到后,会被实例化并注册到 IOC 容器中,成为一个 Bean。
此时,你的 MyWebConfig 对象安静地躺在 Spring 容器里。它的身份有两个:
- 它是一个 Bean。
- 它的类型是
WebMvcConfigurer。
画外音 :如果没有
@Configuration(或@Component),Spring 容器根本不知道这个类的存在,后续的一切都不会发生。
2. 搜集员:DelegatingWebMvcConfiguration
在 Spring MVC 的自动化配置体系中,有一个核心配置类叫 DelegatingWebMvcConfiguration。它是 Spring MVC 能够识别用户自定义配置的"总管家"。
让我们看看它的源码(关键部分):
java
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
// 1. 定义一个"复合对象",用来存放所有的配置类
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
// 2. 【核心魔法】利用 Spring 的自动注入特性
// Spring 会自动去容器里查找 *所有* 类型为 WebMvcConfigurer 的 Bean
// 并把它们打包成一个 List 注入进来
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
// 将搜集到的所有配置类(包括你写的 MyWebConfig),添加到复合对象中
this.configurers.addWebMvcConfigurers(configurers);
}
}
// ...
}
原理解析:
Spring 的依赖注入非常强大。当你对 List<Interface> 类型使用 @Autowired 时,Spring 不会报错,而是会地毯式搜索容器,把所有实现了该接口的 Bean 全部找出来,放入 List 中。
结果: 你写的 MyWebConfig 就这样被"搜集"到了总管家手中。
3. 执行者:委派模式
当 Spring Boot 启动,准备初始化拦截器(Interceptors)时,会调用 DelegatingWebMvcConfiguration 中的 addInterceptors 方法。
但这个"总管家"自己并不干活,它把任务委派 给了那个"复合对象" (WebMvcConfigurerComposite)。
java
// DelegatingWebMvcConfiguration.java
@Override
protected void addInterceptors(InterceptorRegistry registry) {
// 总管家说:我不知道要加什么拦截器,问问那帮配置类吧
this.configurers.addInterceptors(registry);
}
紧接着,复合对象会遍历之前搜集到的所有配置类:
java
// WebMvcConfigurerComposite.java
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 遍历 List,依次调用每一个配置类的 addInterceptors 方法
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}
结果: 循环执行中,你重写的 MyWebConfig.addInterceptors() 方法就被调用了!你的拦截器也就成功注册到了 registry 中。
二、 深入:是谁触发了这一切?
你可能会追问:"那又是谁调用了 DelegatingWebMvcConfiguration 的 addInterceptors 呢?"
这就涉及到了 模板方法模式。
DelegatingWebMvcConfiguration 继承自父类 WebMvcConfigurationSupport。这个父类负责创建 Spring MVC 的核心组件。
以静态资源映射 为例,父类中有一个 @Bean 方法:
java
// WebMvcConfigurationSupport.java
@Bean
public HandlerMapping resourceHandlerMapping(...) {
// 1. 创建注册表
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(...);
// 2. 【调用点】调用 addResourceHandlers
// 虽然这里是在父类写的,但因为子类(Delegating...)重写了它,
// 根据 Java 多态性,实际执行的是子类的逻辑(即去调用所有用户的配置)
addResourceHandlers(registry);
// 3. 返回创建好的 Mapping
return registry.getHandlerMapping();
}
完整链路闭环:
- Spring 启动,准备创建
HandlerMappingBean。 - 执行父类的
@Bean方法。 - 父类方法内部调用
addResourceHandlers(钩子方法)。 - 子类
DelegatingWebMvcConfiguration响应调用。 - 子类委派给
Composite复合对象。 - 复合对象遍历 List,最终调用你写的代码。