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

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

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

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

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

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

1. 引入依赖:

xml 复制代码
    <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. 写配置:

xml 复制代码
    <!-- 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 依赖,但它特殊的点在于:它本身几乎不包含业务代码,只是一个"依赖清单"

xml 复制代码
    <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 的命名规则

类型 命名格式 例子
官方 Starter spring-boot-starter-* spring-boot-starter-webspring-boot-starter-data-jpa
第三方 Starter *-spring-boot-starter mybatis-spring-boot-starterdubbo-spring-boot-starter

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

2.3 Starter 的核心价值

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

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

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

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

三、自动装配:Spring Boot 最核心的机制

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

3.1 入口:@SpringBootApplication

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

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

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

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

3.2 核心机制:扫描 spring.factories

@EnableAutoConfiguration 生效后,Spring Boot 会调用 SpringFactoriesLoader,去所有 jar 包的 META-INF/spring.factories 文件中读取配置

java 复制代码
    // AutoConfigurationImportSelector 中的核心逻辑
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        EnableAutoConfiguration.class,  // 查找 key = EnableAutoConfiguration
        beanClassLoader
    );

打开 spring-boot-autoconfigure 包的 META-INF/spring.factories

text 复制代码
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
    ...

这里列出了 120+ 个自动配置类。

注意 :读取到这些类名只是第一步,它们并不会全部生效。关键在下一步。

3.3 条件注解:决定谁真正生效

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

java 复制代码
    @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 环境
@ConditionalOnNotWebApplication 当前不是 Web 环境

3.4 自动装配全流程(总结)

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

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

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

注意两个关键点:

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

4.2 核心区别对比

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

4.3 代码怎么写

过滤器:

java 复制代码
@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("响应返回");
    }
}

拦截器:

java 复制代码
@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 即可生效):

java 复制代码
@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 文件中读取配置,批量创建对象。这是典型的工厂模式

java 复制代码
// 工厂根据 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、配置属性等)决定走哪条策略。

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

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

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

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

java 复制代码
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()),也可以直接拦截返回。

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

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

5.5 设计模式之间的关系

六、一张图收尾

写在最后

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

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

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

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

相关推荐
CodeSheep28 分钟前
OpenJDK 全面禁止 AI 生成代码!
前端·后端·程序员
IT_陈寒1 小时前
为什么我的Vue组件总是莫名其妙重渲染?
前端·人工智能·后端
lhldsg2 小时前
AI零售系统实战指南:从架构设计到落地部署全解析
java·人工智能·小程序·uni-app·零售
Interview Aid1122 小时前
TikTok OA 四题分享|半小时内 AC,题目基本都是实现题
java·开发语言·算法·面试·职场和发展
mldong2 小时前
换了工作流引擎,前端一行代码没改
java·架构
东风破_9 小时前
danci 2:创建的单词书到底存在哪里?从 Supabase 一路理解 ORM、Drizzle 和 RLS
数据库·后端·node.js
微尘寒风10 小时前
【Git】的安装和使用
java·git
东风破_10 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
y = xⁿ10 小时前
DeepSeek Harness 学习日记:关于Agent接口,工具调用的底层实现
android·java·学习