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 分钟前
Java双亲委派模型工作原理
java
smileSunshineMan2 分钟前
idea启动kafka源码
java·kafka·intellij-idea
利刃大大4 分钟前
【RabbitMQ】重试机制 && TTL && 死信队列
分布式·后端·消息队列·rabbitmq·队列
indexsunny4 分钟前
互联网大厂Java面试实战:核心技术与微服务架构解析
java·数据库·spring boot·缓存·微服务·面试·消息队列
想用offer打牌5 分钟前
非常好用的工具: curl
java·后端·github
·云扬·6 分钟前
ClickHouse数据备份与恢复实战:从基础操作到工具应用
android·java·clickhouse
程序员清风9 分钟前
贝壳一面:Spring是怎么实现的?谈谈你的理解?
java·后端·面试
坚持学习前端日记11 分钟前
后台管理系统文档
java·开发语言·windows·spring boot·python·spring
季风113211 分钟前
29.Axon框架-事件(七)
后端·领域驱动设计
雨中飘荡的记忆12 分钟前
Spring Security入门:从零开始构建安全应用
java·安全·spring