文章目录
- [1、认识 Spring Boot](#1、认识 Spring Boot)
-
- [一、Spring Boot 是什么?](#一、Spring Boot 是什么?)
- [二、Spring Boot 主要解决什么问题?](#二、Spring Boot 主要解决什么问题?)
- [三、Spring Boot 的核心思想](#三、Spring Boot 的核心思想)
-
- [1. 约定优于配置](#1. 约定优于配置)
-
- 如果没有"约定优于配置"会怎样?
- [约定大于配置在Spring Boot中的具体体现](#约定大于配置在Spring Boot中的具体体现)
- [2. 自动配置](#2. 自动配置)
- [3. Starter 启动器](#3. Starter 启动器)
-
- [为什么需要 Starter](#为什么需要 Starter)
- [Starter 的本质是什么](#Starter 的本质是什么)
- 什么是传递依赖
- 例子:spring-boot-starter-web
- [4. 开箱即用](#4. 开箱即用)
-
- [Spring Boot 为什么能做到开箱即用](#Spring Boot 为什么能做到开箱即用)
-
- 1、Starter:把需要的材料准备好
- 2、自动配置:把材料自动组装起来
- 3、约定优于配置:提供合理默认值
- [4、内嵌服务器:不用单独安装 Tomcat](#4、内嵌服务器:不用单独安装 Tomcat)
- [5. 总结:四大核心之间的关系](#5. 总结:四大核心之间的关系)
- [四、Spring Boot 项目的核心启动类](#四、Spring Boot 项目的核心启动类)
- [五、@SpringBootApplication 是什么?](#五、@SpringBootApplication 是什么?)
- 六、为什么启动类通常放在根包?
- [七、SpringApplication.run() 做了什么?](#七、SpringApplication.run() 做了什么?)
- [八、Spring Boot 为什么可以直接运行?](#八、Spring Boot 为什么可以直接运行?)
- [九、Spring Boot 配置文件](#九、Spring Boot 配置文件)
- [十、Spring Boot 的"默认值"和"自定义值"](#十、Spring Boot 的“默认值”和“自定义值”)
- [十一、Spring Boot 的主要组成部分](#十一、Spring Boot 的主要组成部分)
Spring Boot 可以分成下面四层学习:
层级 核心目标 学习结果
第一层:会使用 会创建、配置、启动 Spring Boot 项目 能独立开发普通接口
第二层:懂机制 理解启动、自动配置、条件装配 遇到自动配置问题能自己排查
第三层:会生产化 掌握日志、测试、Actuator、优雅停机 应用能稳定部署和运行
第四层:会扩展 自定义自动配置、Starter、源码调试 能封装公司级公共组件
推荐学习顺序:
1、认识 Spring Boot
一、Spring Boot 是什么?
一句话理解:Spring Boot 是一套帮助我们快速创建、配置、启动和运行 Spring 应用的开发框架。
官方对 Spring Boot 的定位是:让开发者能够轻松创建可以直接运行、适合生产环境的 Spring 应用。
注意:Spring Boot ≠ 完全独立的新框架,Spring Boot = 对 Spring 应用开发过程的自动化和简化。它没有抛弃 Spring,而是把 Spring 应用中大量重复、烦琐的配置工作自动完成。
二、Spring Boot 主要解决什么问题?
在没有 Spring Boot 的情况下,创建一个 Spring 应用通常需要手动处理很多事情:
text
选择各种组件
↓
添加对应依赖
↓
配置组件
↓
配置运行环境
↓
配置服务器
↓
部署应用
↓
启动应用
Spring Boot 将这个过程简化为:
text
选择需要的功能
↓
编写少量配置
↓
运行 main 方法
↓
应用启动
Spring Boot 最主要解决以下几个问题。
| 问题 | Spring Boot 的解决方式 |
|---|---|
| 配置内容太多 | 自动配置 |
| 不知道应该引入哪些组件 | Starter 启动器 |
| 组件版本容易冲突 | 统一管理组件版本 |
| 需要单独安装服务器 | 提供内嵌服务器 |
| 部署流程烦琐 | 可以打包成可执行程序 |
| 不同环境配置麻烦 | 外部化配置 |
| 应用运行状态难以查看 | Actuator 监控能力 |
三、Spring Boot 的核心思想
学习 Spring Boot,首先要理解四个核心思想。
1. 约定优于配置
它的核心思想是:框架提前规定一套常用、合理的默认规则。开发者遵守这些规则时,不需要额外写配置;只有不想使用默认规则时,才需要显式配置。也可以简单理解为:按照框架默认的方式做,框架自动帮你处理;偏离默认方式,才需要自己配置。
注意,它并不是:完全不需要配置。而是:减少重复、机械、没有必要的配置。
Spring Boot 为大量配置提供了默认值。
例如,默认配置文件名称:application.properties、application.yml、application.yaml只要按照 Spring Boot 的约定组织项目,它就能够自动识别并完成很多配置。这意味着:
text
没有特殊需求
↓
直接使用默认配置
有特殊需求
↓
覆盖默认配置
所以 Spring Boot 不是"不需要配置",而是常规情况使用默认配置,特殊情况再自定义配置。
约定与配置的关系
二者并不是互相排斥的。正确关系是:
text
约定提供默认行为
配置覆盖默认行为
执行逻辑可以理解为:
text
用户有没有明确配置?
│
├── 有 → 使用用户配置
│
└── 没有 → 使用框架约定
因此,"约定优于配置"的"优于"不是说:配置没有用。而是说:在能使用合理默认值的情况下,优先通过统一约定减少配置。
约定优于配置不等于"猜测"
框架并不是随便猜测你的想法,而是按照事先确定的规则工作。例如:
text
banner.txt 是 Banner
application.yml 是主配置文件
static 目录用于静态资源
启动类所在包及其子包是默认扫描范围
8080 是默认服务器端口
这些都是明确的规则。所以更准确地说:约定就是框架提前写好的默认配置规则。你没有亲自写配置,不代表没有配置。实际上,很多配置已经存在于框架内部,只是框架替你提供了默认值。
如果没有"约定优于配置"会怎样?
假设我们开发一个最简单的 Web 项目。如果框架没有默认约定,可能需要手动配置:
text
服务器使用什么?
服务器端口是多少?
启动服务器的类在哪里?
Controller 放在哪个包?
静态资源放在哪里?
配置文件叫什么?
日志框架使用什么?
JSON 使用什么组件转换?
错误页面放在哪里?
数据库连接池使用哪一种?
即使所有项目的答案都差不多,也要每次重新写一遍。例如:
xml
<server>
<type>tomcat</type>
<port>8080</port>
</server>
<json>
<converter>jackson</converter>
</json>
<static-resource>
<path>classpath:/static/</path>
</static-resource>
这些配置大多数项目都一样,却必须反复编写。Spring Boot 则提前约定:
- 默认使用嵌入式 Tomcat
- 默认端口是 8080
- 默认使用 Jackson 处理 JSON
- 默认配置文件叫 application.properties 或 application.yml
- 默认静态资源放在 static、public 等目录
- 默认扫描启动类所在包及其子包
因此,开发者通常不需要配置这些内容。
约定大于配置在Spring Boot中的具体体现
Spring Boot 中最典型的例子:默认端口
创建一个 Spring Boot Web 项目,不配置端口,应用默认启动在:8080端口,你不需要写:
yml
server:
port: 8080
因为 Spring Boot 已经约定:Web 服务器默认端口是 8080。
遵守约定: 配置文件中什么都不写,不配置 server.port,启动后使用:http://localhost:8080访问。
覆盖约定: 假如你不想使用 8080,就显式配置:
yml
server:
port: 9090
启动地址就变成:http://localhost:9090
这个例子非常完整地体现了约定优于配置:
| 场景 | 是否需要配置 |
|---|---|
使用默认端口 8080 |
不需要 |
使用自定义端口 9090 |
需要 |
自动配置中的约定优于配置
Spring Boot 最核心的能力之一是:自动配置,它会根据以下信息判断应该配置什么:
- 你引入了哪些依赖
- 类路径下有哪些类
- 你有没有自己创建某些 Bean
- 配置文件里写了什么
- 当前是什么类型的应用
例如,引入 Web 启动器:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot 看到相关依赖后,会按照默认约定准备:
text
Spring MVC
DispatcherServlet
Jackson
嵌入式 Tomcat
参数转换器
异常处理基础设施
静态资源处理器
所以你只需要写:
java
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
启动后就能访问:http://localhost:8080/hello
你没有手动创建 Tomcat,也没有手动配置 DispatcherServlet,但项目仍然可以运行。
配置文件名称的约定
Spring Boot 默认会读取:application.properties或者:application.yml。通常放在:src/main/resources/,完整路径例如:src/main/resources/application.yml
遵守约定: 创建src/main/resources/application.yml,写入:
yml
server:
port: 9090
Spring Boot 会自动读取。你不需要告诉它:配置文件在 resources 目录、配置文件名叫 application.yml。因为这些已经是默认约定。
不遵守约定: 假设你把配置文件命名为:my-config.yml,Spring Boot 默认不会把它当作主配置文件加载。这时必须额外告诉 Spring Boot,请读取 my-config.yml。这就是:遵守约定,不需要配置;改变约定,需要额外配置。
启动类位置的约定
Spring Boot 默认会扫描:启动类所在的包,以及它下面的所有子包。例如项目结构:
text
com.example.demo
├── DemoApplication.java
├── controller
│ └── UserController.java
├── service
│ └── UserService.java
└── repository
└── UserRepository.java
启动类:
java
package com.example.demo;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
这里的启动类位于:com.example.demo,Spring Boot 默认扫描:
text
com.example.demo
com.example.demo.controller
com.example.demo.service
com.example.demo.repository
所以这些类通常都能被自动发现:
java
@RestController
public class UserController {
}
@Service
public class UserService {
}
@Repository
public class UserRepository {
}
你不需要手动配置:
java
@ComponentScan({
"com.example.demo.controller",
"com.example.demo.service",
"com.example.demo.repository"
})
启动类放错位置会怎样?假设项目结构变成:
text
com.example.app
└── DemoApplication.java
com.example.controller
└── UserController.java
启动类所在包是:com.example.app,默认扫描范围是:com.example.app,但是 UserController 位于:com.example.controller,它不是 com.example.app 的子包,因此可能扫描不到。这时有两个解决办法。
方法一:遵守约定,调整包结构,将启动类放在共同的父包:
text
com.example
├── DemoApplication.java
├── controller
│ └── UserController.java
└── service
└── UserService.java
这是更推荐的做法。
方法二:显式配置扫描路径
java
@SpringBootApplication
@ComponentScan("com.example")
public class DemoApplication {
}
这里再次体现:包结构遵守 Spring Boot 的约定,就不需要配置扫描路径;包结构不遵守约定,就需要自己指定。
静态资源目录的约定
Spring Boot 默认会从以下位置寻找静态资源:
text
classpath:/static/
classpath:/public/
classpath:/resources/
classpath:/META-INF/resources/
最常见的是:
text
src/main/resources/static/
例如:
text
src/main/resources/static/logo.png
启动项目后,可以访问:http://localhost:8080/logo.png,不需要写 Controller:
java
@GetMapping("/logo.png")
public byte[] getLogo() {
// 不需要这样做
}
也不需要手动配置静态资源映射,因为 Spring Boot 已经有默认约定。自定义静态资源路径,假如你非要把静态文件放到:src/main/resources/my-files/。那就需要显式配置资源路径,或者自行实现资源映射。因此:static 目录,不是 Java 语法要求,而是 Spring Boot 的目录约定。
自定义 Banner 也是约定优于配置
Spring Boot 的自定义 Banner。Spring Boot 约定启动时自动查找:src/main/resources/banner.txt,只要创建这个文件:src/main/resources/banner.txt,然后写入:
text
==================================
用户管理系统启动中
==================================
启动时就会自动显示。你不需要编写:
java
File bannerFile = new File("banner.txt");
读取文件......
输出文件......
也不需要告诉 Spring Boot:这是 Banner 文件,请在启动时加载它。因为 Spring Boot 已经约定:resources 目录下名为 banner.txt 的文件,就是启动 Banner。如果文件叫:my-banner.txt那就偏离了默认约定,需要显式配置:
yml
spring:
banner:
location: classpath:my-banner.txt
对比一下:
| 文件名 | 是否需要额外配置 |
|---|---|
banner.txt |
不需要 |
my-banner.txt |
需要 |
这就是非常典型的"约定优于配置"。
JSON 转换的约定
假设 Controller 返回一个 Java 对象:
java
@RestController
public class UserController {
@GetMapping("/user")
public User getUser() {
User user = new User();
user.setId(1L);
user.setName("张三");
return user;
}
}
对象:
java
public class User {
private Long id;
private String name;
// getter、setter
}
访问接口时,浏览器收到:
json
{
"id": 1,
"name": "张三"
}
为什么 Java 对象自动变成了 JSON?因为 Spring Boot Web 项目默认约定:
- 接口返回对象时,使用消息转换器处理
- 类路径中存在 Jackson 时,默认使用 Jackson
- @RestController 的返回值默认写入响应体
你不需要每个接口都写:
java
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
return json;
修改默认行为, 假如日期默认输出不符合要求,可以配置:
yml
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
也就是说:
- 普通 JSON 转换使用默认约定
- 特殊格式再进行配置
嵌入式服务器的约定
以前部署 Java Web 项目,通常需要:
text
安装 Tomcat
配置 Tomcat
将项目打成 WAR
把 WAR 放入 Tomcat 的 webapps
启动 Tomcat
Spring Boot Web 项目通常直接内置 Tomcat。只要启动:
java
SpringApplication.run(DemoApplication.class, args);
Tomcat 就会跟着启动。这是因为 Spring Boot 约定:引入 Web 启动器后,如果没有其他特殊设置,就创建嵌入式 Web 服务器。你不需要手动写:
java
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.start();
当然,你也可以更换服务器,例如排除 Tomcat,改用 Jetty。但这是特殊需求,所以需要额外配置依赖。
Bean 名称也有约定
例如:
java
@Service
public class UserService {
}
默认 Bean 名称通常是:userService。也就是:
- 类名:UserService
- 默认 Bean 名:userService
你可以这样获取:
java
Object bean = applicationContext.getBean("userService");
如果你想指定其他名称:
java
@Service("myUserService")
public class UserService {
}
这时 Bean 名称变成:myUserService
因此:
| 写法 | Bean 名 |
|---|---|
@Service |
默认按照类名生成 |
@Service("myUserService") |
使用指定名称 |
这同样是:有默认规则时不需要写;想改变默认规则时再配置。
2. 自动配置
自动配置是 Spring Boot 最核心的能力之一。
Spring Boot 的自动配置核心含义是: Spring Boot 在项目启动时,根据当前项目中已有的依赖、配置项、Bean、运行环境等条件,判断项目需要哪些基础组件,并自动把这些组件创建成 Bean 放入 Spring 容器。
Spring Boot 会根据当前应用中存在的组件、配置项和 Bean,判断应该配置哪些功能。官方文档将其描述为:根据应用中已经加入的依赖,尝试自动配置 Spring 应用。可以把它理解成:
text
Spring Boot 查看当前应用具备哪些条件
↓
判断应该启用哪些配置
↓
自动创建应用需要的对象
例如,当 Spring Boot 发现应用具备某项 Web 功能所需要的条件时,就会自动准备相应的 Web 运行环境。自动配置不是强制配置。例如,你引入:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot 发现项目中出现了:
text
Spring MVC
Servlet API
Tomcat
Jackson
它便推测:这是一个基于 Servlet 的 Web 项目。于是自动配置:
text
嵌入式 Tomcat
Spring MVC
DispatcherServlet
JSON 消息转换器
静态资源处理器
错误页面处理
参数绑定和类型转换等组件
Spring Boot 官方对自动配置的描述也是:根据加入项目的 jar 依赖,尝试自动配置 Spring 应用;例如引入 Web Starter 后,由于类路径中出现了 Tomcat 和 Spring MVC,Spring Boot 会据此配置 Web 应用。
先理解:自动配置到底"自动"了什么
自动配置最主要的工作是:自动向 Spring 容器中注册一些基础 Bean。 例如以前可能需要你手动写:
java
@Configuration
public class WebConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
在 Spring Boot 中,很多这类通用 Bean 不需要你自己创建。你只需要:
yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
Spring Boot 就会读取这些配置,自动创建对应的 DataSource Bean。因此,"自动配置"并不是自动写你的业务代码,也不是自动生成 Controller、Service,而是:自动完成框架基础设施的配置和 Bean 注册。
Spring Boot 的自动配置通常遵循一个重要原则:
text
开发者没有自己配置
↓
Spring Boot 使用默认配置
开发者已经自己配置
↓
Spring Boot 退让,使用开发者配置
这通常被称为:用户配置优先,自动配置退让。
自动配置的入口:@SpringBootApplication
一个标准的 Spring Boot 启动类通常是:
java
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
其中最关键的是:@SpringBootApplication,它本质上组合了三个重要注解:
text
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
也可以近似理解成:
java
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class DemoApplication {
}
Spring Boot 2.7 官方文档说明,@SpringBootApplication 组合了 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan。其中真正开启自动配置机制的是 @EnableAutoConfiguration。
| 注解 | 作用 |
|---|---|
@SpringBootConfiguration |
表示这是 Spring Boot 的主要配置类 |
@EnableAutoConfiguration |
开启 Spring Boot 自动配置 |
@ComponentScan |
扫描项目中自己编写的组件 |
需要特别区分:@ComponentScan负责扫描你自己写的:
text
@Controller
@Service
@Repository
@Component
而:@EnableAutoConfiguration负责加载 Spring Boot 提供的自动配置类。
自动配置的整体运行流程
可以把自动配置过程理解成下面几步。
text
启动 Spring Boot
↓
读取 @SpringBootApplication
↓
发现 @EnableAutoConfiguration
↓
加载大量候选自动配置类
↓
检查每个自动配置类的生效条件
↓
条件满足:注册其中的 Bean
条件不满足:跳过
↓
完成 Spring 容器初始化
再展开一点:
text
1. 找到候选自动配置类
2. 判断项目中有没有相关依赖
3. 判断用户有没有自己创建对应 Bean
4. 判断配置文件中的开关和参数
5. 判断是不是 Web 环境
6. 条件全部满足才执行配置
重点在于:Spring Boot 并不是把所有自动配置全部无条件执行,而是先加载候选配置,再通过条件进行筛选。
Spring Boot 本身提供了大量自动配置类,例如:
text
DataSourceAutoConfiguration
WebMvcAutoConfiguration
JacksonAutoConfiguration
RabbitAutoConfiguration
RedisAutoConfiguration
MailSenderAutoConfiguration
TransactionAutoConfiguration
这些类主要位于:spring-boot-autoconfigure这个 jar 包中。
在 Spring Boot 2.7 中,自动配置候选类可以登记在下面这个文件中:
text
META-INF/spring/
org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件内容可以近似理解为:
java
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
Spring Boot 启动时读取这个名单,知道有哪些自动配置类可以尝试加载。官方文档说明,Spring Boot 2.7 会检查 jar 中的 AutoConfiguration.imports 文件,该文件按行列出候选自动配置类。但是:出现在候选名单中,不代表一定会生效。后面还要判断各种条件。
通过条件注解决定是否生效,自动配置真正的核心是:@Conditional,也就是:符合某些条件,配置才生效。常见的条件注解包括:
| 条件注解 | 含义 |
|---|---|
@ConditionalOnClass |
类路径中存在指定类时生效 |
@ConditionalOnMissingClass |
类路径中不存在指定类时生效 |
@ConditionalOnBean |
容器中存在指定 Bean 时生效 |
@ConditionalOnMissingBean |
容器中不存在指定 Bean 时生效 |
@ConditionalOnProperty |
配置项满足条件时生效 |
@ConditionalOnWebApplication |
当前是 Web 应用时生效 |
@ConditionalOnNotWebApplication |
当前不是 Web 应用时生效 |
@ConditionalOnResource |
指定资源文件存在时生效 |
Spring Boot 官方文档把条件分为类条件、Bean 条件、属性条件、资源条件、Web 应用条件等类别。自动配置类最常使用的就是 @ConditionalOnClass 和 @ConditionalOnMissingBean。
@ConditionalOnClass含义:项目中存在某个类,配置才生效。例如:
java
@Configuration
@ConditionalOnClass(RabbitTemplate.class)
public class RabbitAutoConfiguration {
}
可以理解为:
text
项目中存在 RabbitTemplate 类吗?
│
├── 存在 → 说明项目引入了 RabbitMQ 相关依赖
│ 可以继续配置 RabbitMQ
│
└── 不存在 → 跳过 RabbitMQ 自动配置
为什么要这样做?因为一个普通 Web 项目可能根本不使用 RabbitMQ。如果没有这个条件,Spring Boot 每次启动都尝试创建 RabbitMQ 组件,就会产生大量错误。
@ConditionalOnMissingBean含义:Spring 容器中不存在指定类型的 Bean 时,才创建默认 Bean。例如下面是简化示意:
java
@Configuration
public class SomeAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new DefaultSomeService();
}
}
执行逻辑是:
text
容器中有没有 SomeService?
│
├── 没有 → Spring Boot 创建默认 SomeService
│
└── 有 → 使用用户自己的,不再创建默认对象
官方文档也强调,@ConditionalOnMissingBean 的重要作用,就是允许开发者用自己的 Bean 覆盖自动配置提供的默认实现。
@ConditionalOnProperty含义:根据配置文件中的配置项决定是否启用某个功能。例如:
java
@Configuration
@ConditionalOnProperty(
prefix = "my.feature",
name = "enabled",
havingValue = "true"
)
public class MyFeatureAutoConfiguration {
}
配置:
yml
my:
feature:
enabled: true
这时自动配置生效。如果改成:
yml
my:
feature:
enabled: false
自动配置不生效。执行逻辑:
text
my.feature.enabled 是否等于 true?
│
├── 是 → 加载配置
└── 否 → 跳过配置
@ConditionalOnProperty 可以通过配置属性、期望值以及 matchIfMissing 等参数控制条件。
@ConditionalOnWebApplication含义:只有当前项目是 Web 项目时,配置才生效。例如:
java
@Configuration
@ConditionalOnWebApplication
public class WebAutoConfiguration {
}
如果当前是:Spring MVC Web 应用就执行。如果只是一个:
text
普通定时任务程序
批处理程序
命令行程序
则不会执行 Web 相关配置。
把自动配置写成一段伪代码,自动配置可以理解成下面的逻辑:
java
if (项目中存在某个依赖) {
if (当前环境符合要求) {
if (用户没有自己创建对应的Bean) {
根据application.yml中的配置 {
创建默认Bean;
放入Spring容器;
}
}
}
}
数据源自动配置
引入:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
再引入 MySQL 驱动:
xml
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
配置:
yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
你没有写:
java
@Bean
public DataSource dataSource() {
// 手动创建连接池
}
但是仍然可以注入:
java
@Autowired
private DataSource dataSource;
这是因为 Spring Boot 自动创建了 DataSource Bean。
数据源自动配置大致过程:
text
项目中存在 DataSource 类吗?
│
├── 否 → 不进行数据源配置
│
└── 是
↓
容器中已经有 DataSource Bean 吗?
│
├── 有 → 使用用户自己的
│
└── 没有
↓
读取 spring.datasource 配置
↓
选择连接池实现
↓
创建 DataSource Bean
在 Spring Boot 2.7 中,如果 HikariCP 存在,默认优先选择 HikariCP;而 spring-boot-starter-jdbc 和 spring-boot-starter-data-jpa 默认会带入 HikariCP。
自动配置通常需要同时满足:
text
依赖存在
+
环境正确
+
配置合理
+
用户没有提供替代 Bean
这就是 Spring Boot 自动配置最核心的运行模式。
手写一个简单的"自动配置"
下面通过自己实现一个简化版功能,彻底理解自动配置。
假设有一个类:
java
public class MessageService {
public String send(String message) {
return "发送消息:" + message;
}
}
创建自动配置类:
java
@Configuration
@ConditionalOnClass(MessageService.class)
public class MessageAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MessageService messageService() {
return new MessageService();
}
}
含义是:
text
如果项目中存在 MessageService 类
并且 Spring 容器中没有 MessageService Bean
就自动创建一个 MessageService
没有用户配置,用户直接注入:
java
@RestController
public class MessageController {
private final MessageService messageService;
public MessageController(MessageService messageService) {
this.messageService = messageService;
}
}
Spring Boot 自动配置会创建默认对象,因此可以正常注入。
用户自己提供 Bean
java
@Configuration
public class MyConfig {
@Bean
public MessageService messageService() {
return new CustomMessageService();
}
}
此时容器中已经存在 MessageService。所以自动配置中的:@ConditionalOnMissingBean,不满足,默认对象不会创建。
加入配置开关
java
@Configuration
@ConditionalOnClass(MessageService.class)
@ConditionalOnProperty(
prefix = "message",
name = "enabled",
havingValue = "true",
matchIfMissing = true
)
public class MessageAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MessageService messageService() {
return new MessageService();
}
}
默认不配置时:matchIfMissing = true,所以功能开启。关闭:
yml
message:
enabled: false
这时整个自动配置不生效。这就是一个完整的简化版自动配置:
text
类存在
+
配置项允许
+
用户没有自己的 Bean
=
创建默认 Bean
3. Starter 启动器
在 Spring Boot 中,Starter 通常翻译为:启动器,但它并不是"负责启动 Spring Boot 的程序"。更准确地说:Starter 是为某一种开发场景准备好的依赖集合。 例如,你要开发 Web 接口,需要:
- Spring MVC
- Spring Web
- JSON 转换
- 嵌入式 Tomcat
- 日志组件
你不需要逐个寻找并引入,只需要添加:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot 官方把 Starter 定义为一种方便的"依赖描述器":它为某个开发场景集中提供所需的 Spring 及相关技术依赖,开发者不必四处寻找并复制大量依赖配置。一句话记忆:Starter 就是 Spring Boot 提供的"依赖套餐"、"功能集合"。
为什么需要 Starter
假设没有 Starter,你想开发一个 Web 项目,可能需要自己添加很多依赖:
xml
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!-- Tomcat -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
这还没有完全列完。你还需要考虑:
text
这些依赖的版本是否兼容?
有没有漏掉传递依赖?
Tomcat 和 Spring MVC 版本是否匹配?
Jackson 版本是否匹配?
日志组件是否冲突?
有了 Starter,只需要写:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
它会通过 Maven 的传递依赖,把 Web 开发常用依赖一起引入。所以 Starter 主要解决两个问题:
xml
不用逐个寻找依赖
不用自己组合常用依赖
Starter 的本质是什么
从 Maven 的角度看,Starter 本质上就是:一个提前写好依赖关系的 Maven 模块。 它的核心内容通常不是大量 Java 代码,而是一个类似下面的 pom.xml:
xml
<project>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
</dependencies>
</project>
当然,这是为了理解而写的简化版本。所以当你引入:spring-boot-starter-web,真正发生的是:
text
你的项目
↓
依赖 spring-boot-starter-web
↓
starter-web 又依赖其他模块
↓
Maven 继续下载这些模块
↓
这些 jar 全部进入项目 classpath
以 Spring Boot 2.7 为例,官方说明:
- spring-boot-starter-web 用于 Spring MVC Web 和 REST 应用,默认使用嵌入式 Tomcat。
- spring-boot-starter-jdbc 用于 JDBC,并默认搭配 HikariCP。
- spring-boot-starter-test 用于测试,包含 JUnit Jupiter、Hamcrest、Mockito 等常用测试库。
什么是传递依赖
理解 Starter,必须理解 Maven 的传递依赖。假设:
text
你的项目依赖 A
A 又依赖 B
B 又依赖 C
那么 Maven 会把:A、B、C都加入你的项目。这就是传递依赖。对应到 Starter:
text
你的项目
↓
spring-boot-starter-web
↓
spring-web
spring-webmvc
spring-boot-starter-json
spring-boot-starter-tomcat
spring-boot-starter
↓
更多底层依赖
所以你只在 pom.xml 中写了一个 Starter,但 IDEA 的 Maven 依赖列表中会出现很多 jar。这不是 Spring Boot 偷偷改了 pom.xml,而是 Maven 根据依赖关系,自动解析出了所有传递依赖。
例子:spring-boot-starter-web
引入 Starter:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这个 Starter 面向的场景是:
text
开发 Spring MVC Web 项目
开发 RESTful 接口
启动嵌入式 Web 服务器
进行 JSON 数据转换
Spring Boot 2.7 官方文档说明,spring-boot-starter-web 用于构建基于 Spring MVC 的 Web 和 REST 应用,默认嵌入式容器是 Tomcat。
可以简化理解为它大致引入以下内容:
text
spring-boot-starter-web
│
├── spring-boot-starter
│ ├── Spring Boot 核心组件
│ ├── 自动配置支持
│ ├── 默认日志支持
│ └── YAML 支持
│
├── spring-boot-starter-json
│ └── Jackson 等 JSON 处理组件
│
├── spring-boot-starter-tomcat
│ └── 嵌入式 Tomcat
│
├── spring-web
│ └── Spring 基础 Web 能力
│
└── spring-webmvc
└── Spring MVC 能力
注意,这是为了学习而简化后的依赖树,不是完整依赖列表。
为什么引入后就能写接口?添加 Starter 后,可以直接写:
java
@RestController
public class UserController {
@GetMapping("/user")
public User getUser() {
User user = new User();
user.setId(1L);
user.setName("张三");
return user;
}
}
启动项目后访问:http://localhost:8080/user,得到:
json
{
"id": 1,
"name": "张三"
}
在这个过程中,Starter 为项目准备了所需依赖:Spring MVC、Tomcat、Jackson、Spring Web、日志组件,随后 Spring Boot 自动配置根据这些依赖,自动配置:
text
嵌入式 Tomcat
DispatcherServlet
Spring MVC
JSON 消息转换器
静态资源处理器
因此,这个过程要拆成两个部分理解:
- Starter: 把相关依赖引进来
- 自动配置: 根据这些依赖创建和配置组件
4. 开箱即用
开箱即用,英文是:Out of the Box,可以简单理解为:把东西拿出来后,不需要进行大量安装、组装和配置,就能够直接使用。
在 Spring Boot 中,"开箱即用"通常表示:引入某个 Starter,提供少量必要配置后,Spring Boot 就会自动准备好相关组件,开发者可以直接编写业务代码。
例如开发一个 Web 接口,只需要引入:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
然后写一个 Controller:
java
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello Spring Boot";
}
}
启动项目后访问:http://localhost:8080/hello,就能得到:Hello Spring Boot。你没有手动:
text
安装并配置 Tomcat
创建 DispatcherServlet
配置 Spring MVC
配置请求映射处理器
配置字符串响应转换器
指定服务器默认端口
但项目仍然可以运行。这就是 Spring Boot 所说的:开箱即用。
Spring Boot 为什么能做到开箱即用
Spring Boot 的开箱即用,不是某一个单独功能带来的,而是下面几个机制共同实现的:
text
Starter
+
自动配置
+
约定优于配置
+
内嵌服务器
+
依赖版本管理
完整过程可以理解为:
text
引入 Starter
↓
获得某个场景需要的一组依赖
↓
Spring Boot 自动配置检查项目环境
↓
按照默认约定创建相关 Bean
↓
使用少量配置覆盖必要参数
↓
开发者直接编写业务代码
1、Starter:把需要的材料准备好
前面已经学习过 Starter。Starter 的作用是:为某种开发场景准备好常用依赖。例如:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
它会带来 Web 开发常用的依赖,例如:
text
Spring Web
Spring MVC
Jackson
嵌入式 Tomcat
日志组件
不使用 Starter 时,你可能要一个个寻找和引入这些依赖。使用 Starter 后,只需要引入一个依赖套餐。因此:Starter 负责把材料准备好
2、自动配置:把材料自动组装起来
只有依赖还不够。假设你的项目中有 Tomcat 的 jar,但没有代码创建和启动 Tomcat,它仍然不能自动运行。Spring Boot 的自动配置会根据当前环境判断:
text
项目中有没有 Tomcat?
项目中有没有 Spring MVC?
当前是不是 Web 项目?
用户有没有自定义服务器配置?
用户有没有自己创建相关 Bean?
条件满足后,Spring Boot 自动创建并配置相关组件。例如:
text
Starter 引入 Tomcat
↓
自动配置发现 Tomcat 类存在
↓
创建嵌入式服务器工厂
↓
按照默认端口启动 Tomcat
因此:自动配置负责把材料组装起来
3、约定优于配置:提供合理默认值
Spring Boot 为常见场景提供默认值,例如:
text
服务器默认端口:8080
配置文件默认名称:application.yml
Banner 默认文件名:banner.txt
静态资源默认目录:static
默认 Web 服务器:Tomcat
如果这些默认值符合需求,你不需要配置。例如不写:
yml
server:
port: 8080
Tomcat 仍然会使用 8080 端口。只有想修改时,才需要配置:
yml
server:
port: 9090
因此:约定优于配置负责减少需要填写的参数
4、内嵌服务器:不用单独安装 Tomcat
传统 Java Web 项目经常需要:
text
安装 Tomcat
↓
配置 Tomcat
↓
项目打成 WAR
↓
把 WAR 放入 webapps
↓
启动 Tomcat
Spring Boot Web 项目通常使用嵌入式 Tomcat。项目打成 jar 后,可以直接运行:
linux
java -jar demo.jar
Tomcat 会随着 Spring Boot 应用一起启动。所以你不需要:
text
单独下载 Tomcat
单独配置 Tomcat
把项目复制到 webapps
手动维护外部 Tomcat
这也是 Spring Boot 开箱即用的重要组成部分。
5. 总结:四大核心之间的关系
可以先记住下面这个流程:
text
Starter
负责把需要的依赖准备好
↓
约定优于配置
提供一套默认规则和默认值
↓
自动配置
根据依赖、环境、配置自动创建 Bean
↓
开箱即用
开发者写少量代码就能运行
也可以这样理解:
| 核心 | 主要解决的问题 |
|---|---|
| Starter | 我需要引入哪些依赖? |
| 约定优于配置 | 如果用户没有配置,默认应该怎么做? |
| 自动配置 | 谁来根据环境创建 Tomcat、Spring MVC 等对象? |
| 开箱即用 | 能不能引入依赖后,写少量代码直接运行? |
四、Spring Boot 项目的核心启动类
一个典型的 Spring Boot 启动类如下:
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这里有两个重要部分:
- @SpringBootApplication
- SpringApplication.run(...)
五、@SpringBootApplication 是什么?
@SpringBootApplication 是 Spring Boot 应用最核心的注解之一。
它不是单一功能注解,而是一个组合注解,主要组合了:
java
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
官方文档也给出了这一对应关系。它们分别代表:
| 注解 | 主要作用 |
|---|---|
@SpringBootConfiguration |
表明当前类是 Spring Boot 配置类 |
@EnableAutoConfiguration |
开启 Spring Boot 自动配置 |
@ComponentScan |
扫描并注册应用中的组件 |
因此:@SpringBootApplication,可以大致理解为:
text
这是一个 Spring Boot 应用
+
开启自动配置
+
扫描应用组件
六、为什么启动类通常放在根包?
假设项目结构如下:
text
com.example
├── Application.java
├── controller
├── service
├── config
└── component
启动类放在:com.example.Application。Spring Boot 默认会从启动类所在包开始,扫描它所在的包以及所有子包。扫描范围可以理解为:
text
com.example
├── controller √
├── service √
├── config √
└── component √
如果某个类位于启动类包结构之外:org.demo.service。默认情况下,它可能不会被扫描到。所以一般建议:将启动类放在项目最外层的根包中。
七、SpringApplication.run() 做了什么?
java
SpringApplication.run(Application.class, args);
这行代码不是简单地执行一个普通方法。它负责启动整个 Spring Boot 应用。可以先用简化流程理解:
text
执行 main 方法
↓
创建 SpringApplication
↓
判断当前应用类型
↓
准备运行环境
↓
读取配置文件
↓
创建 Spring 容器
↓
执行自动配置
↓
扫描并注册组件
↓
创建应用中的对象
↓
启动内嵌运行环境
↓
执行启动完成后的扩展逻辑
↓
应用启动完成
官方文档将 SpringApplication 定义为:一种通过 main() 方法引导启动 Spring 应用的便捷方式。
现阶段不需要立刻研究每一步的源码,但要先记住:SpringApplication.run() 是 Spring Boot 应用的总启动入口。
八、Spring Boot 为什么可以直接运行?
传统 Web 应用经常需要:
text
准备服务器
↓
把应用部署到服务器
↓
启动服务器
Spring Boot 可以使用内嵌服务器,把服务器作为应用的一部分运行。 于是启动过程变成:
text
启动 Spring Boot 应用
↓
同时启动内嵌服务器
↓
应用开始接收请求
Spring Boot 官方文档说明,Web 应用可以使用内嵌 Tomcat、Jetty 或 Netty 创建自包含的 HTTP 服务器。
因此,一个 Spring Boot 应用通常可以直接通过启动类运行,而不需要先手动把应用部署到外部服务器。
九、Spring Boot 配置文件
Spring Boot 通常使用以下配置文件:
application.properties、application.yml、application.yaml例如application.yml:
yml
server:
port: 8081
spring:
application:
name: demo
配置文件的主要作用是:
text
修改 Spring Boot 默认配置
配置应用自身参数
配置不同运行环境
控制某些自动配置行为
Spring Boot 支持外部化配置,即同一份应用代码可以在不同环境下使用不同配置;配置来源可以包括配置文件、环境变量和命令行参数等。
十、Spring Boot 的"默认值"和"自定义值"
假设 Spring Boot 默认端口是某个值。应用没有配置端口时:使用 Spring Boot 默认值,应用进行了配置时:
yml
server:
port: 8088
那么:用户配置覆盖默认配置,因此可以把 Spring Boot 配置体系理解成:
text
Spring Boot 提供默认配置
↓
开发者按需进行覆盖
↓
最终形成应用实际配置
学习 Spring Boot 配置时,不只是记住某个属性叫什么,还要理解:
- 这个属性控制哪个功能;
- 不配置时默认行为是什么;
- 配置后改变了什么;
- 是否能通过代码方式自定义;
- 不同配置来源的优先级如何。
十一、Spring Boot 的主要组成部分
从学习角度,可以把 Spring Boot 分成以下部分:
text
Spring Boot
├── 启动机制
├── 自动配置
├── Starter
├── 配置文件
├── 配置绑定
├── 环境与 Profile
├── 日志系统
├── Web 应用支持
├── 测试支持
├── 开发工具
├── Actuator
├── 应用打包
└── 应用运行与生产配置
其中最核心的主线是:
text
启动
↓
加载配置
↓
创建容器
↓
扫描组件
↓
执行自动配置
↓
创建 Bean
↓
启动应用
十二、Spring Boot、Starter、自动配置之间的关系
这是入门阶段最容易混淆的地方。
Starter: 负责准备某个开发场景需要的组件
自动配置: 负责根据当前条件配置这些组件
配置文件: 负责允许开发者修改默认行为
SpringApplication: 负责启动并组织整个应用
它们组合起来:
text
Starter 准备组件
↓
SpringApplication 启动应用
↓
自动配置判断条件
↓
创建并配置相关对象
↓
配置文件覆盖默认值
↓
应用运行