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,最终调用你写的代码
相关推荐
大阿明4 小时前
Spring Boot(快速上手)
java·spring boot·后端
哆啦A梦15885 小时前
Springboot整合MyBatis实现数据库操作
数据库·spring boot·mybatis
bearpping5 小时前
Java进阶,时间与日期,包装类,正则表达式
java
邵奈一5 小时前
清明纪念·时光信笺——项目运行指南
java·实战·项目
sunwenjian8865 小时前
Java进阶——IO 流
java·开发语言·python
sinat_255487815 小时前
读者、作家 Java集合学习笔记
java·笔记·学习
墨香幽梦客5 小时前
API集成技术规范:RESTful与GraphQL在企业系统对接中的应用对比
后端·restful·graphql
皮皮林5515 小时前
如何画出一张优秀的架构图?(老鸟必备)
java
百锦再5 小时前
Java 并发编程进阶,从线程池、锁、AQS 到并发容器与性能调优全解析
java·开发语言·jvm·spring·kafka·tomcat·maven
森林猿6 小时前
java-modbus-读取-modbus4j
java·网络·python