揭秘 Spring Boot 的"魔法"是怎么实现的
第 1 章我们学会了"开车"(用 Spring Boot 写代码)。
但是——你有没有好奇过:
starter-web 依赖,Spring MVC 就能用了?server.port=9090,端口就变了?上一章说 @SpringBootApplication 是三合一注解。现在我们逐个拆开看:
// 📄 @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.controller、com.example.service 都能被扫描到。
想象你在网上买了一堆东西(引入了各种 starter 依赖)。
快递分拣中心(Spring Boot)会:
① 查看清单(读取 spring.factories 文件)
② 检查包裹(用 @Conditional 判断哪些能用)
③ 送到你家(创建 Bean 放入 IoC 容器)
只有你真正买了的东西才会送到,没买的不会送来。
以 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 的核心作用。
Spring Boot 用这些注解来决定"这个配置要不要生效":
选择你的项目引入了哪些依赖,看看哪些自动配置会生效:
Spring Boot 可以把 application.yml 里的配置值自动绑定到 Java 对象上,有三种方式:
// 📄 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)。
不同场景换不同的"配置",而你本人(代码)没有变化。
# 📄 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