← 返回上一页

第2章: 自动配置原理

揭秘 Spring Boot 的"魔法"是怎么实现的

学习进度 0/6

为什么要学自动配置原理?

🚗 比喻:开车 vs 考驾照

第 1 章我们学会了"开车"(用 Spring Boot 写代码)。
但是——你有没有好奇过:

  • 我明明没有配 Tomcat,它是怎么自己启动的?
  • 我加了个 starter-web 依赖,Spring MVC 就能用了?
  • application.yml 里写个 server.port=9090,端口就变了?
🔍 这一章我们就来"拆车看发动机"——搞懂 Spring Boot 的核心机制。
💡 学完这章你能:
  • 面试时自信地回答"Spring Boot 自动配置原理"
  • 出了 Bug 知道去哪儿排查配置问题
  • 能写自己的自定义自动配置

拆解 @SpringBootApplication

上一章说 @SpringBootApplication 是三合一注解。现在我们逐个拆开看:

🎁 比喻:Spring Boot 送你一个"大礼包"

📋
@Configuration
"我是配置清单"
⚙️
@EnableAutoConfiguration
"帮你自动装好一切"
🔍
@ComponentScan
"扫描你写的组件"
// 📄 @SpringBootApplication 的源码结构(简化版) @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) // ① 标记为配置类(等同于 @Configuration) // 作用:告诉 Spring "这个类里可以定义 @Bean" @SpringBootConfiguration // ② 开启自动配置(Spring Boot 的核心魔法!) // 作用:根据你引入的依赖,自动帮你配置好对应的功能 @EnableAutoConfiguration // ③ 组件扫描(扫描你写的 @Controller/@Service/@Repository 等) // 作用:从主类所在包开始,递归扫描所有子包 @ComponentScan public @interface SpringBootApplication { // ... }
⚠️ 重要:主启动类的位置很关键!

@ComponentScan 默认扫描主类所在的包及其子包。所以主启动类一定要放在根包(如 com.example),这样所有 com.example.controllercom.example.service 都能被扫描到。

com.example/ ← 主启动类放这里!
├── Application.java ← @SpringBootApplication
├── controller/ ← ✅ 能被扫描到
├── service/     ← ✅ 能被扫描到
└── repository/ ← ✅ 能被扫描到

com.other/      ← ❌ 扫描不到!(不在 com.example 下)

自动配置的核心流程

📦 比喻:快递分拣中心

想象你在网上买了一堆东西(引入了各种 starter 依赖)。
快递分拣中心(Spring Boot)会:
查看清单(读取 spring.factories 文件)
检查包裹(用 @Conditional 判断哪些能用)
送到你家(创建 Bean 放入 IoC 容器)
只有你真正买了的东西才会送到,没买的不会送来。

🎮 互动:点击按钮模拟自动配置的完整过程
点击按钮查看自动配置过程...

流程详解(面试重点 🔥)

启动应用 → 触发 @EnableAutoConfiguration
SpringApplication.run() 启动时,@EnableAutoConfiguration 开始工作
读取 META-INF/spring.factories 文件
从所有 JAR 包中找到 "自动配置清单",一共 130+ 个配置类
@Conditional 条件判断
逐个检查每个配置类的条件注解,只有满足条件的才会生效
创建 Bean 放入 IoC 容器
满足条件的配置类中的 @Bean 方法被执行,Bean 注册到容器中
应用启动完成!
Tomcat、Spring MVC、JSON 处理器等全部自动配置好了

来看一个真实的自动配置类

以 DataSourceAutoConfiguration(数据源自动配置)为例:

// 📄 DataSourceAutoConfiguration.java(简化版) // 这是 Spring Boot 内置的自动配置类 @Configuration // 这是一个配置类 // ⭐ 条件注解:只有当类路径中存在 DataSource 类时,才生效 // 翻译:只有你引入了数据库相关依赖(如 mysql-connector),这个配置才会启用 @ConditionalOnClass(DataSource.class) // ⭐ 条件注解:只有当容器中没有人手动配置 DataSource Bean 时,才生效 // 翻译:如果你自己配了,Spring Boot 就不管了(尊重你的选择!) @ConditionalOnMissingBean(DataSource.class) // 绑定配置文件中 spring.datasource.xxx 的属性 @EnableConfigurationProperties(DataSourceProperties.class) public class DataSourceAutoConfiguration { @Bean // 额外条件:配置文件中必须有 spring.datasource.type 属性 @ConditionalOnProperty(name = "spring.datasource.type") public DataSource dataSource(DataSourceProperties properties) { // 根据 application.yml 中的配置,自动创建数据源 return DataSourceBuilder.create() .url(properties.getUrl()) // 从 spring.datasource.url 读取 .username(properties.getUsername()) // 从 spring.datasource.username 读取 .password(properties.getPassword()) // 从 spring.datasource.password 读取 .build(); } }
💡 关键总结:

自动配置 = 有条件的配置。Spring Boot 不是什么都配,而是「你有什么依赖,我就配什么」。这就是 @Conditional 的核心作用。

@Conditional 条件注解大全

Spring Boot 用这些注解来决定"这个配置要不要生效":

@ConditionalOnClass
类路径中存在某个类时生效
例:加了 MySQL 驱动 → 数据源配置生效
@ConditionalOnMissingClass
类路径中不存在某个类时生效
例:没加某个 SDK → 用备选方案
@ConditionalOnBean
容器中存在某个 Bean 时生效
例:存在 DataSource → 配置事务管理器
@ConditionalOnMissingBean
容器中不存在某个 Bean 时生效
例:你没自己配 → Spring Boot 帮你配
@ConditionalOnProperty
配置文件中存在指定属性时生效
例:写了 spring.redis.host → Redis 配置生效
@ConditionalOnWebApplication
Web 应用时生效
例:引入 starter-web → Web 相关配置生效
🎮 互动:模拟条件判断过程

选择你的项目引入了哪些依赖,看看哪些自动配置会生效:

配置绑定:让 yml 变成 Java 对象

Spring Boot 可以把 application.yml 里的配置值自动绑定到 Java 对象上,有三种方式:

方式1:@ConfigurationProperties(推荐) 方式2:@Value 方式3:Environment
✅ 推荐:适合管理一组相关配置
// 📄 MyAppProperties.java — 配置属性类 // 把 application.yml 中 myapp.xxx 的值自动绑定到这个类的字段上 @Component // 注册为 Spring Bean @ConfigurationProperties(prefix = "myapp") // 绑定 myapp 前缀的配置 public class MyAppProperties { private String name = "默认应用"; // 对应 myapp.name private String version = "1.0.0"; // 对应 myapp.version private boolean enabled = true; // 对应 myapp.enabled // getter/setter(Spring 通过 setter 注入配置值) public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } }
# 📄 application.yml myapp: name: 我的应用 # → MyAppProperties.name version: 2.0.0 # → MyAppProperties.version enabled: true # → MyAppProperties.enabled
适合注入单个简单值
// 📄 AppConfig.java — 使用 @Value 注入单个值 @Component public class AppConfig { // 从配置文件读取 app.name 的值 @Value("${app.name}") private String appName; // 如果配置文件没写,用 1.0.0 作为默认值 @Value("${app.version:1.0.0}") private String version; // 冒号后面是默认值,如果配置文件没写就用默认值 @Value("${app.enabled:true}") private boolean enabled; }
适合动态读取(运行时决定读哪个配置)
// 📄 ConfigReader.java — 使用 Environment 动态读取 @Component public class ConfigReader { @Autowired private Environment env; // Spring 提供的环境对象 public void readConfig() { // 读取字符串配置 String appName = env.getProperty("app.name"); // 读取整数配置,并提供默认值 int port = env.getProperty("server.port", Integer.class, 8080); // 读取布尔配置 boolean enabled = env.getProperty("app.enabled", Boolean.class, true); } }

多环境配置:开发、测试、生产一键切换

🎭 比喻:换衣服出门

上班穿正装(prod),在家穿睡衣(dev),运动穿运动服(test)。
不同场景换不同的"配置",而你本人(代码)没有变化。

src/main/resources/
├── application.yml          ← 公共配置(写一次到处用)
├── application-dev.yml    ← 开发环境(本地调试用)
├── application-test.yml   ← 测试环境
└── application-prod.yml   ← 生产环境(上线用)
# 📄 application.yml — 主配置文件 spring: profiles: active: dev # ⭐ 在这里切换环境!改成 prod 就用生产配置 app: name: SpringBoot Demo # 公共配置,所有环境都用
# 📄 application-dev.yml — 开发环境(本地调试用) server: port: 8080 # 本地端口 spring: datasource: url: jdbc:mysql://localhost:3306/dev_db # 本地数据库 username: root password: 123456 logging: level: root: DEBUG # 开发时打印更多日志
# 📄 application-prod.yml — 生产环境(上线用) server: port: 80 # 生产端口(80 或 443) spring: datasource: url: jdbc:mysql://prod-server:3306/prod_db # 生产数据库 username: prod_user password: ${DB_PASSWORD} # ⭐ 从环境变量读取,不硬编码密码! logging: level: root: WARN # 生产环境只打印警告和错误
# 切换环境的 4 种方式: # 方式1:在 application.yml 中写(最常用) spring.profiles.active=prod # 方式2:启动命令行参数(优先级最高) java -jar app.jar --spring.profiles.active=prod # 方式3:设置环境变量(适合 Docker/K8s) export SPRING_PROFILES_ACTIVE=prod # 方式4:IDE 运行参数 -Dspring.profiles.active=prod

📝 学完检测

第 1 题:Spring Boot 自动配置类是从哪个文件加载的?
A. application.yml
B. pom.xml
C. META-INF/spring.factories
D. web.xml
第 2 题:@ConditionalOnMissingBean 的作用是?
A. 当 Bean 不存在时抛异常
B. 当容器中不存在指定 Bean 时,当前配置才生效
C. 删除容器中的某个 Bean
D. 创建一个空的 Bean
第 3 题:以下哪种方式切换环境的优先级最高?
A. application.yml 中写 spring.profiles.active
B. 命令行参数 --spring.profiles.active
C. 环境变量 SPRING_PROFILES_ACTIVE
D. IDE 运行参数 -Dspring.profiles.active

本章小结

← 上一章: SpringBoot 入门 返回上一页