SpringBoot手动配置:WebMvcConfigurer接口实现类的生效原理

正文内容

前言

在 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 容器里。它的身份有两个:

  1. 它是一个 Bean。
  2. 它的类型是 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 中。


二、 深入:是谁触发了这一切?

你可能会追问:"那又是谁调用了 DelegatingWebMvcConfigurationaddInterceptors 呢?"

这就涉及到了 模板方法模式

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();
}

完整链路闭环:

  1. Spring 启动,准备创建 HandlerMapping Bean。
  2. 执行父类的 @Bean 方法。
  3. 父类方法内部调用 addResourceHandlers(钩子方法)。
  4. 子类 DelegatingWebMvcConfiguration 响应调用。
  5. 子类委派给 Composite 复合对象。
  6. 复合对象遍历 List,最终调用你写的代码
相关推荐
野生技术架构师1 小时前
SpringBoot项目使用Redis对用户IP进行接口限流
spring boot·redis·bootstrap
程序员皮皮林1 小时前
SpringBoot + nmap4j 获取端口信息
java·spring boot·后端
小二·1 小时前
Spring框架入门:Spring 中注解支持详解
java·后端·spring
计算机学长felix1 小时前
基于SpringBoot的“某学院教室资产管理系统”的设计与实现(源码+数据库+文档+PPT)
数据库·spring boot·后端
豐儀麟阁贵1 小时前
8.6运行时异常
java·开发语言
桃子叔叔1 小时前
Prompt Engineering完全指南:从基础到高阶技术实战
java·服务器·prompt
CRUD酱1 小时前
RabbitMQ是如何确保消息的可靠性的?
java·python·rabbitmq
百花~1 小时前
Spring IoC&DI~
java·后端·spring
独自破碎E1 小时前
矩阵区间更新TLE?试试二维差分
java·线性代数·矩阵