Spring Boot 核心原理(五):配置管理怎么玩?从基础到多环境再到配置中心
上一篇我们拆解了 Spring Boot 的启动流程,搞懂了 "一行代码如何启动应用"。但实际开发中还有个高频需求:不同环境(开发、测试、生产)需要不同配置(比如数据库地址、服务器端口),怎么灵活切换?命令行参数、环境变量怎么覆盖配置文件?@Value和@ConfigurationProperties该选哪个?
这些问题的答案,都在 Spring Boot 的 "配置管理" 机制里。它不仅支持多种配置源,还能实现多环境动态切换,甚至对接分布式配置中心 ------ 今天,我们从 "基础配置" 到 "高级用法",彻底搞懂 Spring Boot 的配置管理,在项目中轻松搞定各种配置场景。
一、先理清:配置管理的核心目标
为什么需要专门的配置管理?举个实际场景:
- 开发环境(dev):数据库用本地 MySQL(localhost:3306),服务器端口 8080;
- 测试环境(test):数据库用测试服务器 MySQL(192.168.1.10:3306),端口 8081;
- 生产环境(prod):数据库用生产集群 MySQL(10.0.0.10:3306),端口 80,且密码需要从环境变量注入(避免硬编码)。
如果每次切换环境都手动改配置文件,不仅效率低,还容易出错。Spring Boot 配置管理的核心目标就是:
- 多源配置:支持配置文件、命令行、环境变量、配置中心等多种配置来源;
- 优先级覆盖:高优先级配置自动覆盖低优先级(如命令行参数覆盖配置文件);
- 多环境切换:一键切换 dev/test/prod 等环境,无需修改配置内容; 便捷注入:轻松将配置参数注入到代码中(避免硬编码)。
二、基础:配置文件类型与加载优先级
Spring Boot 支持多种配置文件,最常用的是properties和yaml,还有yml(yaml 的简写,两者语法一致)。先搞懂它们的区别和加载顺序。
1. 两种配置文件:properties vs yaml
两者都是键值对配置,但 yaml 的层级结构更清晰,支持列表、对象等复杂类型,是目前主流选择。
(1)properties:传统键值对
格式:key=value,层级用.分隔,不支持列表直接配置(需用特殊格式)。
示例(application.properties):
java
# 服务器配置
server.port=8080
server.servlet.context-path=/demo
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/dev_db
spring.datasource.username=root
spring.datasource.password=123456
# Redis配置(列表需用索引)
spring.redis.cluster.nodes[0]=192.168.1.1:6379
spring.redis.cluster.nodes[1]=192.168.1.2:6379
(2)yaml:层级结构(推荐)
格式:key: value(冒号后必须加空格),层级用缩进(空格,不能用 Tab),支持列表、对象,可读性更强。示例(application.yml):
java
# 服务器配置(层级清晰)
server:
port: 8080
servlet:
context-path: /demo
# 数据库配置(对象格式)
spring:
datasource:
url: jdbc:mysql://localhost:3306/dev_db
username: root
password: 123456
# Redis配置(列表格式,更直观)
redis:
cluster:
nodes:
- 192.168.1.1:6379
- 192.168.1.2:6379
注意点:
yaml 对缩进敏感,同一层级缩进必须一致(建议 2 个空格);
字符串无需加引号(特殊字符如\n需加双引号);
一个项目中可以同时存在 properties 和 yaml,但 yaml 优先级更高(同目录下)。
2. 配置文件加载优先级(关键)
Spring Boot 会从多个位置加载配置文件,高优先级配置会覆盖低优先级,顺序从高到低如下(重要,必须记):
- 命令行参数: 启动时通过--key=value指定(如java -jar demo.jar --server.port=8081);
- 系统环境变量: 如 Linux 的export SPRING_DATASOURCE_URL=xxx,Windows 的set
SPRING_DATASOURCE_URL=xxx; - 系统属性: 通过System.setProperty("key", "value")设置;
- application-{profile}.yml/properties: 激活的环境配置(如application-dev.yml);
- application.yml/properties: 默认配置(所有环境共享);
- bootstrap.yml/properties: 引导配置(优先级最低,常用于 Spring Cloud 配置中心)。
实战验证:
如果application.yml配置server.port=8080,命令行启动时加--server.port=8081,最终端口是 8081(命令行优先级更高)。
三、核心:配置属性注入的两种方式
配置文件写好后,需要将参数注入到 Java 代码中使用。Spring Boot 提供两种主流方式:@Value和@ConfigurationProperties,各有适用场景,千万别用混了。
1. @Value:单个简单属性注入
适合注入单个、简单类型 的属性(字符串、数字、布尔值),支持 SpEL 表达式(如计算、获取系统属性)。
实战案例:注入服务器端口和数据库 URL
java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component // 必须交给Spring管理
public class ServerConfig {
// 注入端口,默认值8080(冒号后是默认值,配置缺失时生效)
@Value("${server.port:8080}")
private Integer port;
// 注入数据库URL,无默认值(配置缺失会报错)
@Value("${spring.datasource.url}")
private String dbUrl;
// 支持SpEL表达式:获取系统属性(user.home)
@Value("#{systemProperties['user.home']}")
private String userHome;
// 支持SpEL计算:10+20=30
@Value("#{10+20}")
private Integer sum;
// Getter省略(供其他类调用)
}
优缺点:
- 优点:简单直观,适合单个属性,支持 SpEL;
- 缺点:不支持复杂类型(列表、对象),需逐个注入(属性多了代码冗余),不支持属性校验。
2. @ConfigurationProperties:批量复杂属性注入
适合批量、复杂类型 的属性注入(如对象、列表),支持属性校验(如非空、格式校验),IDE 有自动提示(方便开发)。
实战案例 1:注入数据源配置(对象类型)
java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
@Component
@Validated // 开启属性校验(必须加)
// 绑定配置文件中"spring.datasource"前缀的属性
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceConfig {
// 校验URL非空(配置缺失会报错,提示信息自定义)
@NotBlank(message = "数据库URL不能为空")
private String url;
@NotBlank(message = "数据库用户名不能为空")
private String username;
// 密码非必须(不校验)
private String password;
// Getter和Setter必须有(否则无法绑定配置,idea会提示)
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
实战案例 2:注入 Redis 集群配置(列表类型)
java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class RedisClusterConfig {
// 注入列表(对应配置文件中的nodes列表)
private List<String> nodes;
// 注入整数(默认值3)
private Integer maxRedirects = 3;
// Getter和Setter省略
}
优缺点:
- 优点:批量注入、支持复杂类型(列表 / 对象)、支持属性校验、IDE 自动提示;
- 缺点:不支持 SpEL 表达式,配置前缀固定(灵活性略低)。
3. 两种方式对比(表格清晰版)
| 特性 | @Value | @ConfigurationProperties |
|---|---|---|
| 注入方式 | 单个属性逐个注入 | 批量注入(前缀匹配) |
| 复杂类型支持 | 不支持(列表 / 对象) | 支持 |
| 属性校验 | 不支持 | 支持(需 @Validated) |
| IDE 自动提示 | 不支持 | 支持 |
| SpEL 表达式支持 | 支持 | 不支持 |
| 适用场景 | 简单单个属性 | 复杂配置(数据源 / Redis 等) |
选型建议:
- 注入单个简单属性(如端口、日志级别)→ 用@Value;
- 注入复杂配置(如数据源、Redis 集群、自定义业务配置)→ 用@ConfigurationProperties。
四、实战:多环境配置(dev/test/prod)
实际开发中,不同环境的配置差异很大(比如开发环境用本地数据库,生产环境用集群数据库),Spring Boot 的 "profile" 机制能实现多环境一键切换。
1. 配置文件拆分(推荐)
将不同环境的配置拆分成单独文件,命名格式为application-{profile}.yml/properties,默认配置放application.yml。
项目结构示例:
xml
src/main/resources/
├─ application.yml # 公共配置(所有环境共享)
├─ application-dev.yml # 开发环境配置
├─ application-test.yml # 测试环境配置
└─ application-prod.yml # 生产环境配置
配置内容示例:
- 公共配置(application.yml):所有环境共享的配置(如上下文路径、Redis 超时时间)
yaml
server:
servlet:
context-path: /demo # 所有环境共用此路径
spring:
redis:
timeout: 3000ms # 所有环境共用此超时时间
profiles:
active: dev # 默认激活开发环境(可改)
- 开发环境(application-dev.yml):开发专属配置
yaml
server:
port: 8080 # 开发环境端口8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/dev_db # 本地数据库
username: root
password: 123456
- 生产环境(application-prod.yml):生产专属配置
yaml
server:
port: 80 # 生产环境端口80(HTTP默认端口)
spring:
datasource:
url: jdbc:mysql://10.0.0.10:3306/prod_db # 生产集群数据库
username: prod_user
password: ${DB_PASSWORD} # 从环境变量注入密码(避免硬编码)
2. 单文件拆分(yaml 专属)
如果不想创建多个文件,yaml 支持用---分隔不同环境的配置,放在同一个application.yml中。
示例:
yaml
# 公共配置
server:
servlet:
context-path: /demo
spring:
redis:
timeout: 3000ms
# 开发环境(profile=dev)
---
spring:
profiles: dev
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/dev_db
username: root
password: 123456
# 生产环境(profile=prod)
---
spring:
profiles: prod
server:
port: 80
spring:
datasource:
url: jdbc:mysql://10.0.0.10:3306/prod_db
username: prod_user
password: ${DB_PASSWORD}
3. 激活指定环境(5 种方式)
激活不同环境的方式有多种,按优先级从高到低如下:
命令行参数(最常用,灵活):启动时指定,优先级最高
bash
# 激活测试环境
java -jar demo.jar --spring.profiles.active=test
# 激活生产环境(同时指定端口)
java -jar demo.jar --spring.profiles.active=prod --server.port=80
环境变量(容器化部署常用):Linux/Mac/Windows 通用
bash
# Linux/Mac
export SPRING_PROFILES_ACTIVE=prod
java -jar demo.jar
# Windows(cmd)
set SPRING_PROFILES_ACTIVE=prod
java -jar demo.jar
配置文件指定(默认方式):在application.yml中配置
yaml
spring:
profiles:
active: dev # 默认激活开发环境
系统属性(程序内设置):通过System.setProperty设置
java
public class DemoApplication {
public static void main(String[] args) {
// 激活测试环境
System.setProperty("spring.profiles.active", "test");
SpringApplication.run(DemoApplication.class, args);
}
}
程序内通过 SpringApplication 设置:更灵活的代码控制
java
public class DemoApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DemoApplication.class);
// 激活生产环境
app.setAdditionalProfiles("prod");
app.run(args);
}
}
注意: 如果同时用多种方式激活,高优先级会覆盖低优先级(如命令行覆盖配置文件)。
五、高级:外部配置源(对接配置中心)
单机项目用本地配置文件足够,但分布式项目(如微服务)需要集中管理配置(避免每个服务改配置文件),这时候就需要对接配置中心(如 Nacos、Apollo、Spring Cloud Config)。
这里以常用的Nacos为例,演示如何集成配置中心,实现配置动态刷新(改配置不用重启服务)。
1. 步骤 1:引入 Nacos 依赖
在 pom.xml 中引入 Nacos 配置中心依赖(需先搭建 Nacos 服务端,本地可启动 Nacos 单机版):
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.0.4.0</version> <!-- 版本需与Spring Boot匹配 -->
</dependency>
2. 步骤 2:配置 Nacos 地址(bootstrap.yml)
配置中心的地址需要放在bootstrap.yml中(引导配置,比application.yml加载早):
yaml
spring:
application:
name: demo-service # 服务名(Nacos中配置文件的前缀)
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 # Nacos服务端地址
file-extension: yml # 配置文件格式(yml/properties)
namespace: dev # 命名空间(隔离环境,如dev/test/prod)
group: DEFAULT_GROUP # 配置分组(默认DEFAULT_GROUP)
3. 步骤 3:在 Nacos 控制台创建配置
1,启动 Nacos 服务端,访问http://localhost:8848/nacos(默认账号密码 nacos/nacos);
2,进入 "配置管理→配置列表",点击 "+" 创建配置:
- Data ID:demo-service-dev.yml(格式:服务名-{profile}.格式);
- 配置格式:YAML;
- 配置内容(和本地application-dev.yml一致):
yaml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/dev_db
username: root
password: 123456
4. 步骤 4:动态刷新配置
默认情况下,Nacos 配置变更后,Spring Boot 不会自动刷新,需要在注入配置的类上加@RefreshScope注解:
java
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@RefreshScope // 开启配置动态刷新
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceConfig {
private String url;
private String username;
private String password;
// Getter和Setter省略
}
验证动态刷新:
修改 Nacos 中demo-service-dev.yml的spring.datasource.url为新地址;
无需重启服务,代码中获取的url会自动更新为新值。
六、常见配置问题及解决方案
1,yaml 配置不生效?
- 检查缩进(必须用空格,不能用 Tab);
- 检查键值对格式(key: value冒号后必须加空格);
- 检查文件名是否正确(如application-dev.yml,profile 不能拼错)。
2,@ConfigurationProperties 注入失败?
- 确保类上有@Component(交给 Spring 管理);
- 确保有 Getter 和 Setter(yaml 绑定依赖 setter);
- 检查配置前缀是否正确(prefix与配置文件中的前缀一致)。
3,配置中心配置不生效?
- 检查bootstrap.yml是否存在(配置中心地址必须放这里);
- 检查 Data ID 是否正确(服务名-{profile}.格式);
- 检查 Nacos 服务端地址是否能通(ping 127.0.0.1:8848)。
七、总结
本次学习我们彻底搞懂了 Spring Boot 的配置管理:
- 配置文件支持 properties 和 yaml,yaml 更推荐(层级清晰);
- 配置优先级:命令行 > 环境变量 > 环境配置 > 默认配置;
- 属性注入:@Value适合简单单个属性,@ConfigurationProperties适合复杂批量配置;
- 多环境配置用 profile 机制,激活方式灵活;
- 分布式项目对接配置中心(如 Nacos),实现配置集中管理和动态刷新。