从"怎么用"到"为什么这么设计",彻底搞懂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-web、spring-boot-starter-data-jpa |
| 第三方 Starter | *-spring-boot-starter |
mybatis-spring-boot-starter、dubbo-spring-boot-starter |
看到这个命名,你就知道它是个 Starter。
2.3 Starter 的核心价值
Starter = 依赖聚合 + 触发自动配置
引入一个 Starter,做了两件事:
- 帮你把该场景需要的所有 jar 包一次性引入
- 触发 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 设计模式之间的关系

六、一张图收尾

写在最后
这篇文章没有追求大而全,而是尽量把逻辑链串清楚:
- Starter 帮你聚合依赖、触发自动配置
- 自动装配 通过 spring.factories 发现候选,通过 @Conditional 按需生效
- Filter 和 Interceptor 是 Web 层两种不同粒度的拦截机制,搞清楚它们的执行顺序和适用场景
- 设计模式 是 Spring Boot 的架构骨架,理解它们才能理解"为什么这么设计"
把这四条线串起来,Spring Boot 在你面前就不再是黑盒了。
如果你觉得这篇文章讲清楚了,点个赞支持下。如果有疑问,欢迎评论区交流~