先给结论:SpringBoot 不是一门新的编程语言,也不是一个可以用“增删改查”来概括的业务框架。它是一个用来简化 Spring 应用创建、配置、启动和部署的项目基础框架。很多开发者在刚接触它时,会误以为 SpringBoot 就是 Spring MVC 的升级版,实际上它做的事情比这更靠前:它把“要不要放一个配置文件”“这个类交给 Spring 管理还是我自己 new”“Web 服务要装到什么容器里”这些原本需要开发者做决定的重复工作,用一套约定和自动配置机制提前处理掉了。
这篇内容会从实际开发者的视角,把 SpringBoot 是什么、它做了哪些事情、一个最小项目是如何由它拆解出来的、自动装配的原理是什么这几个问题逐个讲清楚。如果你是刚开始学 SpringBoot,或者在准备相关面试,建议把环境和示例项目跟着敲一遍。整个过程不依赖复杂的业务场景,只要能跑通一个返回字符串的接口,后面的配置、打包、监控都会好理解很多。
1. 先搞清楚 SpringBoot 在解决什么问题
1.1 Spring 框架时期的开发痛点
在 SpringBoot 出现之前,用 Spring 开发一个 Web 项目通常要经历下面这些步骤:下载依赖,维护一个复杂的 XML 配置文件,选择 Web 容器,把项目打成 war 包,再部署到外部 Tomcat 或 Jetty 上。哪怕只是一个简单的接口,也需要确认几件事:Spring 容器有没有扫描到这个 Controller、事务管理器是否初始化、配置文件里的数据库连接是否写对。
下面是一段早期 Spring MVC 项目常见的 XML 配置片段,它的目的只是开启组件扫描并配置一个最朴素的视图解析器。放到今天看,这些内容并不是业务逻辑,但缺了又不行:
<context:component-scan base-package="com.example.demo"/> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean>这段配置看起来不复杂,可当项目里出现数据源、事务、拦截器、消息队列、定时任务后,XML 文件会变得越来越长,而且很容易出现“改了一个 bean 的 id,运行到第 100 行才报错”的问题。
除此之外,依赖管理也是痛点。Spring 项目本身由多个模块组成:spring-core、spring-context、spring-web、spring-webmvc、spring-jdbc 等。每个模块都有自己的版本号,不同版本的组合不一定兼容。开发者在 pom.xml 里往往要手动排列十几个依赖坐标,稍不留神就会引入重复依赖或者版本冲突。
1.2 SpringBoot 的定义与核心目标
SpringBoot 是一个基于 Spring 框架的快速开发基础设施,官方一直强调它的几个核心目标:创建可以独立运行的 Spring 应用、直接内嵌 Web 容器、提供“起步依赖”简化 Maven/Gradle 配置、通过自动配置减少手写配置、同时提供生产环境需要的监控能力。
通俗地说,SpringBoot 不是一个业务框架,而是一层“自动装配器”。它扫描到你引入了 spring-boot-starter-web,就认为你准备构建一个 Web 应用,于是自动注册 DispatcherServlet、内嵌 Tomcat、配置默认的 JSON 序列化和错误处理;它扫描到 classpath 里有 H2 和 JdbcTemplate,就会在没有任何 SQL 配置的情况下,先帮你注册一个内存数据源。这一切都发生在应用启动的几秒内。
这种设计带来了一个明显变化:以前是“用户告诉 Spring 容器怎么做”,现在是“SpringBoot 根据依赖猜测用户大概率要怎么做,然后提前做好,用户可以在配置文件中覆盖”。
1.3 SpringBoot 与 Spring Framework、Spring MVC 的关系
很多人会把 SpringBoot 和 Spring Framework 混在一起。更准确的关系是:Spring Framework 是底层容器和生态,Spring MVC 是 Web 开发模块,而 SpringBoot 是建立在它们之上的“开箱即用”启动器。SpringBoot 并没有替代 Spring 的依赖注入和 AOP 能力,它只是用更少的配置把这些能力组织起来。
下面用一个表格区分它们各自负责的层次:
| 组件 | 角色 | 典型问题 |
|---|---|---|
| Spring Framework | IoC 容器、AOP、事务、数据访问抽象 | 对象如何创建、依赖如何注入 |
| Spring MVC | Web 层 MVC 框架 | 请求如何路由到 Controller、如何返回视图 |
| SpringBoot | 自动化装配、依赖管理、启动与部署 | 项目怎么快速初始化、容错和监控怎么办 |
在这个划分下,SpringBoot 更像项目的“施工方”:把 Spring 的各模块从散装零件变成一整栋能直接入住的房子。后面讲自动装配原理时,也会再回到这个比喻。
1.4 学 SpringBoot 之前需要做哪些准备
建议至少先掌握以下内容:Java 基础语法和常用集合、Maven 依赖坐标的基本概念、Spring 的依赖注入和注解、HTTP 请求与响应的基本流程。如果完全没有 Spring 基础,直接学 SpringBoot 也能跑通示例,但遇到“为什么 Bean 没有注入”“为什么自动配置没有生效”这类问题时,会很难定位。
准备源码示例时,也建议准备一个能够自由创建目录的 IDE。IntelliJ IDEA 和 Eclipse 都可以,区别只是创建向导不同。重点不是编辑器,而是能通过 Maven 命令运行项目。
2. SpringBoot 真正替你做了哪些事情
2.1 依赖管理:起步依赖与版本仲裁
SpringBoot 接管依赖管理的方式是“起步依赖”和“父 POM”。在 Maven 项目中,常见做法是让当前项目继承 spring-boot-starter-parent,然后在 dependencies 里只需要写网络坐标,不用写版本号。版本号由父 POM 统一管理。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>注意:这里使用 2.7.18 是为了让示例在 JDK 8 下也能运行。如果你用的是 JDK 17 或更高版本,可以考虑使用 Spring Boot 3.x 版本。真正落地项目前,要确认本地 JDK、Maven 和 SpringBoot 版本之间的兼容矩阵。
这个机制带来的收益很直接:以往手动引入 spring-web、spring-webmvc、jackson-databind、tomcat-embed-core 等一大堆依赖,现在一个 spring-boot-starter-web 就全部带进来了。版本冲突的概率也大幅下降,因为父 POM 里已经定义了一组经过测试的组合。
2.2 自动配置:从添加依赖到服务可用
自动配置是 SpringBoot 最核心的“做了哪些事情”。放在 Web 项目里,它至少包含了这几件工作:
- 检测 classpath 中是否存在 spring-webmvc;
- 如果存在,自动注册 DispatcherServlet;
- 如果没有手动定义 Tomcat 等容器,自动创建内嵌容器;
- 自动注册请求映射处理器、消息转换器和基础错误页面。
换个更常见的例子。加入 spring-boot-starter-data-jpa 并在 classpath 中放一个 H2 数据库依赖后,SpringBoot 就会自动配置 DataSource、EntityManagerFactory 和 TransactionManager。你不需要写一行数据库连接参数,就能先让程序启动。当然,生产环境一定会在配置文件中把连接信息覆盖掉,但“先能跑,再改配置”的开发节奏,确实把很多前置工作省掉了。
为表达“自动配置是可覆盖的”,下面是一个最小配置文件:
spring.application.name=demo server.port=8080如果暂时不写 server.port,默认就是 8080。这说明 SpringBoot 对绝大多数配置项都提供了默认值。
2.3 内嵌容器:一条命令启动 Web 服务
传统 Spring MVC 项目需要先安装 Tomcat,再把 war 包放到 webapps 目录下。SpringBoot 内嵌容器机制把这一步换成了直接执行 main 方法,或者执行 java -jar。
mvn spring-boot:run或者先打包:
mvn clean package java -jar target/demo-0.0.1-SNAPSHOT.jar命令执行后,SpringBoot 会在当前进程内启动 Tomcat。对于学习环境,这种“免安装”非常友好;对于生产环境,也意味着只需要安装 JDK 和上传一个 jar,就能把应用跑起来,容器和应用的版本绑定关系由 java -jar 后面这个包决定。
2.4 外部化配置:properties、yaml、环境变量和命令行参数
SpringBoot 的配置体系允许同一个配置项在多个位置出现,并且有固定的优先级。大致顺序是:命令行参数、环境变量、application-{profile}.yml、application.yml、默认配置。理解这个顺序,能解决很多“我改了配置为什么不生效”的问题。
spring: profiles: active: dev server: port: 8081比如上面这段配置把端口改为 8081。如果启动时再带一个 --server.port=8082 参数,最终生效的会是 8082。这不是框架的 bug,而是“外部配置优先级更高”的刻意设计,目的是让同一份 jar 在不同环境使用不同配置。
2.5 生产级特性:健康检查和监控
SpringBoot 还做了传统 Spring MVC 项目需要额外集成才能完成的事:监控和健康检查。引入 spring-boot-starter-actuator 后,应用会暴露一些端点和指标。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>默认情况下,/actuator/health 可以访问,返回状态通常是 UP。如果需要更完整的指标,可以通过配置暴露:
management.endpoints.web.exposure.include=health,info,metrics这一点的意义是:应用被部署到服务器后,运维可以直接用 HTTP 请求探测它是否健康,而不是去服务器上翻日志。很多容器化部署脚本里的 health check,就是用这个接口做的。
3. 用一个最小项目验证 SpringBoot 的工作过程
3.1 环境准备与版本选择
学习阶段建议准备下面这些环境要求。不需要最新的版本,但需要保证三个版本之间互相兼容。
| 软件 | 版本建议 | 说明 |
|---|---|---|
| JDK | 8 或 17 | Spring Boot 2.7.x 支持 JDK 8,3.x 需要 JDK 17 |
| Maven | 3.6 以上 | 负责依赖下载和项目构建 |
| IDE | IDEA 或 Eclipse | 用于编辑代码、查看报错 |
| SpringBoot | 2.7.x 或 3.x | 建议选自己能找到资料的稳定版 |
如果你使用的 IDE 自带 Spring Initializr,创建项目会更方便。如果不想安装工具,也可以在 start.spring.io 页面生成压缩包,然后再用 Maven 导入。
3.2 通过 Maven 搭建项目骨架
为了让原理更清楚,这里先不完全依赖 IDE 向导,直接手写一个最小项目。目录如下:
demo ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── com/example/demo/DemoApplication.java │ │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com/example/demo/DemoApplicationTests.javapom.xml 至少需要包含三块:父 POM、web 起步依赖、spring-boot-maven-plugin。下面是一个适用于 JDK 8 的最小例子:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>这里的关键是 packaging 为 jar,而不是 war。因为 SpringBoot 支持内嵌容器,jar 包也能通过内嵌 Tomcat 对外提供服务。
3.3 编写启动类和示例控制器
启动类是入口。下面这个类是 SpringBoot 应用的最小启动类:
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }然后写一个最简单的 Controller,用来验证请求映射是否生效:
package com.example.demo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello SpringBoot"; } }这里没有写任何 Servlet 配置,也没有写组件扫描路径。@SpringBootApplication 默认扫描当前类所在的包和子包,所以 HelloController 放在 com.example.demo 下面可以被自动发现。
3.4 启动、打包和验证
在项目根目录下执行:
mvn spring-boot:run启动日志中会出现 SpringBoot 的启动横幅,并且能看到类似下面的关键信息:
Tomcat started on port(s): 8080 (http) Started DemoApplication in 1.452 seconds (JVM running for 1.856)这说明内嵌 Tomcat 已经监听 8080 端口。在浏览器或 curl 中访问:
curl http://localhost:8080/hello正常返回:
Hello SpringBoot如果想模拟生产环境部署,可以执行:
mvn clean package java -jar target/demo-0.0.1-SNAPSHOT.jar执行后应用会以独立进程方式运行。停止进程时,在终端按 Ctrl + C,或者执行 kill 命令。当前这个验证链路已经覆盖了 SpringBoot 最常见的三个能力:依赖管理、自动配置、内嵌容器。
为了更清晰地看到“配置覆盖默认值”的作用,可以在 application.yml 中加入一行:
server: port: 9090重新启动后,端口会从 8080 变成 9090。如果直接修改依赖中的配置却没有生效,需要再检查配置优先级。
4. 自动装配原理:SpringBoot 为什么能“替你做决定”
4.1 @SpringBootApplication 的三个组成部分
@SpringBootApplication 是一个组合注解。从源码角度看,它等于 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 三个注解的组合。
- @SpringBootConfiguration 其实是一个 @Configuration,表示当前类是一个配置类;
- @ComponentScan 负责扫描当前包及子包下的 @Component、@Service、@Repository、@Controller 等;
- @EnableAutoConfiguration 是自动配置的启动开关。
只写一个 @SpringBootApplication,就同时打开了组件扫描和自动配置。如果项目需要更精准的控制,也可以不用这个组合注解,改为拆开写,但实际项目里很少这样做。
4.2 自动配置类是怎么被加载的
@EnableAutoConfiguration 的内部逻辑是导入一个 AutoConfigurationImportSelector。在 SpringBoot 2.7 之前,自动配置类的全限定名写在 META-INF/spring.factories 文件中,key 是 EnableAutoConfiguration;在较新版本里,则写在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中。
用户自己的依赖也可以参与自动配置。比如自定义一个 starter,只要在 resources/META-INF 下提供上面文件,并写下自动配置类的完整类名,SpringBoot 启动时就会读到它。
这些自动配置类通常都带 @AutoConfiguration 注解和条件注解。以数据源自动配置为例:
@AutoConfiguration @ConditionalOnClass(DataSource.class) public class DataSourceAutoConfiguration { // ... }4.3 条件注解如何避免配置冲突
自动配置最怕两件事:多配了、和用户手动配置的 Bean 冲突。SpringBoot 使用条件注解来解决这一类问题。
常用条件注解包括:
- @ConditionalOnClass:classpath 中存在某个类才生效;
- @ConditionalOnMissingBean:容器中不存在某个 Bean 时才生效;
- @ConditionalOnProperty:配置文件中存在指定属性时才生效;
- @ConditionalOnWebApplication:当前应用是 Web 应用时才生效。
比如用户自己在配置类中定义了一个 DataSource:
@Bean @ConditionalOnMissingBean public DataSource dataSource() { return new HikariDataSource(); }SpringBoot 的默认数据源配置看到容器里已经有 DataSource,就不会再覆盖。如果项目里出现“我自己定义的 Bean 没生效”“自动配置把我写的类覆盖了”这类问题,先检查条件注解和 Bean 名称是否冲突。
4.4 自动装配原理对排错的意义
理解了自动装配原理后,遇到“为什么这个功能没生效”时,第一个动作不是去改代码,而是去查对应的自动配置类是否生效。启动日志中如果开启了 debug 模式:
debug=true日志里会出现 Positive matches 和 Negative matches,分别列出哪些自动配置生效了、哪些被判定为不生效以及原因。这是排查 SpringBoot 自动配置问题最直观的入口。
5. 使用 SpringBoot 时最容易踩的四个坑
5.1 SpringBoot 版本太高,依赖还是旧写法导致编译失败
现象:项目从 SpringBoot 2.x 升级到 3.x 后,很多 import javax.servlet.* 的代码突然编译失败。
原因:SpringBoot 3.x 基于 Spring Framework 6,Java EE 的包名从 javax.* 迁移到了 jakarta.*。还在用 javax.servlet.http.HttpServletRequest 的旧代码自然无法通过编译。
检查方式:先看报错中 import 语句漏出的包名,再确认当前 SpringBoot 版本对应的 Jakarta EE 版本。
处理方式:要么把 import 的 javax 换成 jakarta,要么暂时不要升级到不兼容的版本。如果看到“springboot 版本太高”这类报错,优先去官方迁移文档里查找包名变更列表,不要盲目删掉依赖。
5.2 application.yml 不提示或配置不生效
现象:在 IDEA 中编辑 application.yml 时,spring.datasource.url 等配置没有自动提示,修改端口后重启又发现端口没变。
原因:前者可能是缺少 spring-boot-configuration-processor 依赖,后者可能是修改到了非当前 profile 的文件,或者配置项被命令行参数覆盖。
检查方式:确认当前激活的 profile,执行 env 命令查看环境变量,检查启动命令里是否带了 --server.port。
处理方式:IDEA 里先重新导入 Maven 项目;SpringBoot 项目如果需要自定义配置项的 IDE 提示,可以引入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>配置是否生效,可以通过 Actuator 的 /actuator/env 端点查看配置来源和值。
5.3 内嵌容器端口占用导致启动失败
现象:启动报错信息中包含 Port 8080 was already in use。
原因:另一个进程占用了 8080 端口,或者上一次启动的应用没有完全停掉。
检查方式:Linux 或 macOS 下用 netstat -ano | grep 8080,Windows 下用 netstat -ano | findstr 8080,找到占用端口的 PID 后确认进程。
处理方式:改配置文件里的 server.port,或者杀掉占用端口的进程。注意不要在生产环境里随意 kill 不认识的进程,先确认进程归属。
5.4 自动配置被覆盖但不自知
现象:明明配置了数据源连接参数,应用启动后还是报错指向默认的内存数据库;或者某个默认的 ObjectMapper 始终是框架默认的,自己定义的序列化规则没有生效。
原因:自动配置类有生效条件,而用户自定义 Bean 可能因为包路径不在扫描范围内,或者条件注解没有匹配上,导致自动配置仍然认为“没人接管”。
检查方式:开启 debug=true,查看 Positive matches 和 Negative matches;再检查自定义 Bean 是否被 Spring 容器扫描到,必要时通过 /actuator/beans 查看 Bean 是否存在。
处理方式:让自定义配置类放在启动类同包或子包下,或者通过 @Import 显式引入;如果确实需要排除某个自动配置类,可以使用 exclude 属性:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})但不要一遇到问题就 exclude。先搞清楚自动配置为何生效或失效,比自己手动关闭更有价值。
用表格汇总一下这四类问题:
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 升级版本后 javax 报错 | Java EE 迁移到 Jakarta | 定位 import 报错 | 替换包名或暂缓升级 |
| yml 没提示、配置没生效 | 缺依赖或 profile/参数覆盖 | 查看激活 profile、Actuator env | 重导 Maven、检查优先级 |
| 端口占用启动失败 | 端口被其他进程占用 | netstat / lsof 查端口 | 改端口或清理进程 |
| 自动配置覆盖自定义 Bean | 条件注解和扫描路径不匹配 | debug=true 查 matches | 调整扫描包或排除配置类 |
6. 从入门到落地:路线图、检查清单和扩展方向
6.1 入门学习路线图
第一个阶段是“跑通”:搭建最小项目,理解 @SpringBootApplication 和 starter 的作用,写一个能返回 JSON 的接口。第二个阶段是“接入”:引入数据库、Redis、消息队列等中间件,理解自动配置改变之后,配置文件和 Bean 该如何调整。第三个阶段是“深入”:读一遍自动配置源码,掌握条件注解、配置属性绑定,能够排查自定义 starter 和框架冲突。第四个阶段是“治理”:关注单元测试、打包、监控、日志、多环境配置和容器部署。
这个过程不必追求一天学完。先让第一阶段形成肌肉记忆,再逐步扩大范围。
6.2 环境搭建检查清单
以下清单适合在开始第一个 SpringBoot 项目前逐项确认:
- JDK 版本是否在 SpringBoot 支持范围内;
- Maven 是否配置了可用的镜像仓库;
- IDE 是否安装了 Lombok 插件(如果用的话);
- 是否整理过本地 Maven 仓库缓存,避免依赖下载到一半出现问题;
- 是否明确项目使用 JDK 8 还是 17+,因为这会直接决定 SpringBoot 主版本;
- 是否知道当前项目激活的 profile 和端口是否冲突。
这些检查项看起来基础,但它们往往比代码本身更容易拖慢上手速度。
6.3 学习环境与生产环境的差异
学习环境通常用 spring-boot:run 直接跑,配置写在本地 application.yml 里。生产环境则完全不同:配置需要外置化,至少要支持环境变量或配置中心;应用需要用日志框架输出到固定目录;健康检查要能被外部探针调用;进程要由 systemd、Docker 或容器编排平台管理;数据库密码通常不会直接写在配置文件中,而是通过密钥管理服务注入。
即使是同一个 SpringBoot 应用,也不能只验证“能启动”,还要验证“启动后依赖的中间件是否可用、配置来源是否正确、异常时能否快速定位”。正因为如此,Actuator 和结构化日志在正式项目里几乎是标配。
6.4 初学 SpringBoot 时值得关注的方向
结合社区里高频出现的问题,下面几个方向很适合作为后续学习切入口:
- 自动装配原理:把 @EnableAutoConfiguration 的加载流程和条件注解读透;
- 配置属性绑定:用 @ConfigurationProperties 管理一组业务配置,而不是散落到处 @Value;
- 单元测试:使用 @SpringBootTest 和 MockMvc 测试 Controller,避免只跑通 main 方法就认为“没问题”;
- 文件上传下载、定时任务、WebSocket、工作流引擎等业务场景,都建议在一个最小可运行项目里先复现,再接入真实业务;
- 打包与部署:理解 jar 和 war 的差异,学会将 SpringBoot 项目构建成镜像并部署到容器环境。
回到开头的问题:SpringBoot 是什么,做了哪些事情?它不是一个神秘框架,它做的最核心的三件事是依赖管理、自动装配和简化启动部署。第一篇内容把这三件事的骨架搭出来,后续遇到具体场景时,再沿着“某个 starter 引入了什么依赖、对应自动配置类何时生效、配置项如何覆盖默认值”这条线索深入,学习效率会高很多。