一篇讲清楚Spring Boot:自动装配、启动器、过滤器、拦截器、设计模式

553 阅读9分钟

从“怎么用”到“为什么这么设计”,彻底搞懂Spring Boot

Spring Boot 已经成为 Java 开发的事实标准,但很多人对它的理解停留在“引入 starter 就能用”的层面。至于“为什么引入一个依赖就能自动生效”,始终是个黑盒。

这篇文章不堆砌概念,用一条逻辑线把 Spring Boot 的核心机制串起来,争取一篇讲透。

一、先回答一个最根本的问题:Spring Boot 到底解决了什么?

在 Spring Boot 出现之前,用 Spring 开发一个 Web 项目,你需要做这些事:

1. 引入依赖:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
    </dependency>
    <!-- 还要操心版本号对不对得上 -->

2. 写配置:


    <!-- web.xml -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
    </servlet>

    <!-- spring-mvc.xml -->
    <mvc:annotation-driven/>
    <context:component-scan base-package="com.demo"/>
    <bean class="InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

每一个项目都在重复这些几乎一模一样的配置。这就是 Spring Boot 要解决的问题——消除样板式的配置和依赖管理,让开发者专注于业务本身。

Spring Boot 的解决方案概括为三个核心:

核心机制解决的问题
Starter(启动器)依赖聚合,不再手动维护版本和依赖列表
自动装配配置自动生成,不再手动写 XML/Java Config
内嵌容器打包成 jar 直接运行,不再部署 war 到 Tomcat

下面我们逐个拆解。

二、Starter:一切从引入一个依赖开始

2.1 Starter 是什么?

Starter 是一个 Maven 依赖,但它特殊的点在于:它本身几乎不包含业务代码,只是一个“依赖清单” 。


    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

引入这一个依赖,传递依赖会带来:

  • spring-webmvc(Spring MVC 框架)
  • jackson-databind(JSON 序列化)
  • tomcat-embed-core(内嵌 Tomcat)
  • spring-boot-autoconfigure(自动配置)
  • 以及它们的兼容版本

你什么都不用配,一个 Web 应用就 ready 了。

2.2 Starter 的命名规则

类型命名格式例子
官方 Starterspring-boot-starter-*spring-boot-starter-web、spring-boot-starter-data-jpa
第三方 Starter*-spring-boot-startermybatis-spring-boot-starter、dubbo-spring-boot-starter

看到这个命名,你就知道它是个 Starter。

2.3 Starter 的核心价值

Starter = 依赖聚合 + 触发自动配置

引入一个 Starter,做了两件事:

  1. 帮你把该场景需要的所有 jar 包一次性引入
  2. 触发 Spring Boot 的自动装配机制,让这些 jar 包的配置自动生效

这就是 Starter 的设计逻辑——场景化封装。想用 Web 功能?引入 web-starter。想用数据库?引入 data-jpa-starter。按需取用,开箱即用。

三、自动装配:Spring Boot 最核心的机制(2.x vs 3.x)

引入 starter 只是第一步,真正让配置自动生效的是自动装配(Auto-Configuration) 。

这是 Spring Boot 3.0 变化最大的地方——自动配置类的注册方式被彻底重写了。

3.1 入口不变:@SpringBootApplication

无论 2.x 还是 3.x,入口都是一样的:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@SpringBootApplication 是一个组合注解,它包含了三个核心注解:

@SpringBootConfiguration  // 等价于 @Configuration——这是个配置类
@EnableAutoConfiguration   // 开启自动装配——关键!
@ComponentScan            // 扫描当前包及子包的组件
public @interface SpringBootApplication {
}

真正起作用的是 @EnableAutoConfiguration——自动装配的总开关。

3.2 核心区别:配置文件的“搬家”

这是 2.x 和 3.x 最本质的区别——自动配置类注册到了不同的文件里。

Spring Boot 2.x:spring.factories

在 Spring Boot 2.x 中,自动配置类注册在 META-INF/spring.factories 文件中。

文件路径:src/main/resources/META-INF/spring.factories

文件内容(Properties 格式,Key-Value,多个类用逗号分隔):

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
com.example.MyAutoConfiguration

EnableAutoConfiguration 是固定的 Key,Value 是自动配置类的全限定名列表。

Spring Boot 3.x:AutoConfiguration.imports

从 Spring Boot 3.0 开始,spring.factories 被彻底废弃,改用新的 AutoConfiguration.imports 文件。

文件路径:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

文件内容(纯文本列表,每行一个类名,不需要 Key,不需要逗号):

org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
com.example.MyAutoConfiguration

注意:  Spring Boot 2.7 是一个过渡版本,同时支持两种方式,但使用 spring.factories 时会打出弃用警告(deprecation warning)。Spring Boot 3.0+ 只认 .imports 文件,在 spring.factories 里写 EnableAutoConfiguration 会被静默跳过。

3.3 加载机制的变化

Spring Boot 2.x:SpringFactoriesLoader

@EnableAutoConfiguration 通过 @Import(AutoConfigurationImportSelector.class) 导入配置选择器。

AutoConfigurationImportSelector 内部调用 SpringFactoriesLoader.loadFactoryNames(),读取所有 jar 包中 META-INF/spring.factories 文件里 key 为 EnableAutoConfiguration 的配置类列表。

// 2.x 核心逻辑
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
    EnableAutoConfiguration.class, 
    beanClassLoader
);

Spring Boot 3.x:ImportCandidates

Spring Boot 3.x 改用 @Import(AutoConfigurationGroup.class),底层通过 ImportCandidates.load() 来加载 AutoConfiguration.imports 文件中的配置类。

// 3.x 核心逻辑(Spring Boot 3.2.x)
List<String> autoConfigurations = ImportCandidates.load(
    AutoConfiguration.class, 
    getBeanClassLoader()
).getCandidates();

注意:Spring Boot 3.x 中 spring.factories 文件仍然存在,但只用于 ApplicationContextInitializer、ApplicationListener 等非自动配置条目。如果你在 spring.factories 里写 EnableAutoConfiguration,Spring Boot 3 会静默跳过——这就是很多项目升级后配置不生效的根源。

3.4 条件注解:决定谁真正生效(2.x 和 3.x 通用)

无论 2.x 还是 3.x,条件注解的机制是一致的——读取到配置类只是第一步,它们并不会全部生效。

如果 120 多个配置类全部生效,必然会有 Bean 冲突、性能浪费。Spring Boot 用 @Conditional 系列注解来做按需加载。

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
public class DataSourceAutoConfiguration {
    // 数据源自动配置
}

解读这三个条件:

注解条件含义
@ConditionalOnClass(DataSource.class)类路径有 DataSource没有 JDBC 驱动就不配置数据源
@ConditionalOnMissingBean(...)容器没有 ConnectionFactory用户自己配了就用用户的
@ConditionalOnProperty(...)配置了 spring.datasource.url没配 URL 就不配置

只有当所有条件都满足时,这个自动配置类才会生效。

常见条件注解速查表:

注解生效条件
@ConditionalOnClass类路径存在指定类
@ConditionalOnMissingClass类路径不存在指定类
@ConditionalOnBean容器中存在指定 Bean
@ConditionalOnMissingBean容器中不存在指定 Bean
@ConditionalOnProperty配置文件中指定属性满足条件
@ConditionalOnWebApplication当前是 Web 环境

3.5 看 2.x 和 3.x 自动装配对比总结

对比维度Spring Boot 2.xSpring Boot 3.x
配置文件位置META-INF/spring.factoriesMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件格式Key=Value(类名逗号分隔)纯类名列表(换行分隔)
加载机制SpringFactoriesLoaderImportCandidates
注解推荐通常用 @Configuration推荐用 @AutoConfiguration(3.x 专属)
兼容性支持旧版不再支持从 spring.factories 读取自动配置
过渡版本—Spring Boot 2.7 同时支持两种方式

3.6 为什么要改?

  1. 性能提升:旧的 spring.factories 文件通常包含各种类型的配置(监听器、环境后处理器、自动配置等),文件解析需要读取所有内容。新的 .imports 文件只关注自动配置,加载更高效。
  2. 更清晰:直接列出类名,不再需要 Key-Value 的解析和逗号分割,减少了出错的可能。
  3. SPI 规范化:新的机制采用了更加标准的 SPI(Service Provider Interface)风格,目录结构更清晰。

3.4 自动装配流程

2.x版本

metool_mermaid (1).png

3.x版本

metool_mermaid (2).png

一句话概括:扫描 spring.factories 找候选 → 条件注解过滤 → 满足条件的才生效。

四、过滤器(Filter)和拦截器(Interceptor)

4.1 先看它们在请求链路中的位置

deepseek_mermaid_20260831_00f15e.png

注意两个关键点:

  • Filter 在 DispatcherServlet 之前执行,比拦截器更早接触到请求
  • Interceptor 在 Controller 前后执行,更靠近业务逻辑

4.2 核心区别对比

对比项FilterInterceptor
所属规范Servlet 规范(Java EE)Spring MVC 框架
是否依赖 Spring否是
执行时机DispatcherServlet 之前Controller 前后
拦截范围所有请求(含静态资源如 .css、.js)只拦截进入 Controller 的请求
能否注入 Spring Bean不能直接注入可以(@Autowired)
典型用途字符编码过滤、跨域处理(CORS)、XSS 防护登录校验、权限控制、操作日志

4.3 代码怎么写

过滤器:


@Component
public class LogFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        System.out.println("请求到达:" + req.getRequestURI());
        chain.doFilter(request, response);  // 放行,继续往下走
        System.out.println("响应返回");
    }
}

拦截器:

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
                             Object handler) throws Exception {
        // 返回 true 放行,false 拦截
        Object user = request.getSession().getAttribute("user");
        if (user == null) {
            response.sendRedirect("/login");
            return false;
        }
        return true;
    }
}

注册拦截器(Filter 不需要注册,@Component 即可生效):

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/admin/**")      // 拦截 /admin/ 下的所有请求
                .excludePathPatterns("/login");     // 放行登录接口
    }
}

4.4 为什么 Filter 不能直接注入 @Autowired?

因为 Filter 是 Servlet 容器(如 Tomcat)管理的,在 Spring 容器初始化之前就已经创建完成了。而 Interceptor 是 Spring 管理的,和 Spring Bean 生命周期同步。

如果你想在 Filter 里用 Spring Bean,可以通过 DelegatingFilterProxy 包装,或者用 FilterRegistrationBean 注册时把 Filter 声明为 Spring Bean(Spring Boot 的 @Component 方式本质上就是这个)。

4.5 选型建议

场景选什么
登录状态校验Interceptor(需要注入 UserService)
权限控制Interceptor
字符编码设置Filter(Servlet 层级,处理原始请求)
跨域处理Filter(CORS 是 Servlet 层面的)
请求日志都可以,Filter 更全面(含静态资源)

五、设计模式:看看 Spring Boot 的“骨架”

5.1 工厂模式——SpringFactoriesLoader

SpringFactoriesLoader 根据 key(如 EnableAutoConfiguration.class)从多个 spring.factories 文件中读取配置,批量创建对象。这是典型的工厂模式。

// 工厂根据 key 生产对象
List<String> classNames = SpringFactoriesLoader.loadFactoryNames(key, classLoader);
for (String name : classNames) {
    Class<?> clazz = Class.forName(name);
    Object instance = clazz.getDeclaredConstructor().newInstance();
    // ...
}

5.2 策略模式——@Conditional 条件注解

每个 @Conditional 注解对应一种判断策略,Spring 根据当前环境(类路径、容器 Bean、配置属性等)决定走哪条策略。

@ConditionalOnClass(DataSource.class)    // 策略1:检查类路径
@ConditionalOnMissingBean                 // 策略2:检查容器
@ConditionalOnProperty                    // 策略3:检查配置文件

不同策略组合,决定最终配置是否生效。

5.3 模板方法模式——AbstractApplicationContext.refresh()

Spring 容器启动的核心方法 refresh() 定义了整个流程的算法骨架,某些步骤交给子类去具体实现。

public void refresh() throws BeansException {
    // 这些步骤的顺序是固定的
    prepareRefresh();                     // 准备刷新(子类可扩展)
    obtainFreshBeanFactory();             // 获取 BeanFactory
    prepareBeanFactory();                 // 准备 BeanFactory(子类可扩展)
    postProcessBeanFactory(beanFactory);  // ★ 钩子方法,子类可重写
    invokeBeanFactoryPostProcessors();    // 调用 BeanFactory 后处理器
    registerBeanPostProcessors();         // 注册 Bean 后处理器
    initMessageSource();                  // 初始化消息源
    // ...
}

子类(如 AnnotationConfigApplicationContext)通过重写 postProcessBeanFactory 等钩子方法,注入自定义逻辑,而整体流程骨架保持不变。

5.4 责任链模式——FilterChain

多个 Filter 组成一个链条,请求沿着链条依次经过每个 Filter,每个 Filter 可以选择处理请求后放行(chain.doFilter()),也可以直接拦截返回。

public void doFilter(ServletRequest request, ServletResponse response, 
                     FilterChain chain) {
    // 前置处理
    log.info("Filter 前置");
    // 放行到下一个 Filter 或最终资源
    chain.doFilter(request, response);
    // 后置处理
    log.info("Filter 后置");
}

这是责任链模式最经典的实现。

5.5 设计模式之间的关系

deepseek_mermaid_20260831_eea0fc.png

六、一张图收尾

deepseek_mermaid_20260831_f3786f.png

写在最后

这篇文章没有追求大而全,而是尽量把逻辑链串清楚:

  1. Starter 帮你聚合依赖、触发自动配置
  2. 自动装配 通过 spring.factories 发现候选,通过 @Conditional 按需生效
  3. Filter 和 Interceptor 是 Web 层两种不同粒度的拦截机制,搞清楚它们的执行顺序和适用场景
  4. 设计模式 是 Spring Boot 的架构骨架,理解它们才能理解“为什么这么设计”

把这四条线串起来,Spring Boot 在你面前就不再是黑盒了。

如果你觉得这篇文章讲清楚了,点个赞支持下。如果有疑问,欢迎评论区交流~