配置管理
Spring Boot 提供了强大的外部化配置支持,允许开发者将配置与代码分离,在不同环境中使用相同的应用代码。本章将深入讲解 Spring Boot 的配置管理机制。
外部化配置概述
什么是外部化配置?
外部化配置是将应用程序的配置信息从代码中分离出来,存储在外部文件或环境变量中。这样做的好处:
- 环境隔离:同一份代码可以在开发、测试、生产环境运行
- 配置热更新:无需重新编译即可修改配置
- 安全性:敏感信息可以不包含在代码库中
- 灵活性:支持多种配置来源和格式
配置加载顺序
Spring Boot 按照特定的顺序加载配置,后面的配置会覆盖前面的同名配置:
优先级从低到高:
1. 默认属性(SpringApplication.setDefaultProperties)
2. @PropertySource 注解
3. 应用配置文件(application.properties/yml)
├── JAR 包内的配置文件
├── JAR 包内的 Profile 配置文件
├── JAR 包外的配置文件
└── JAR 包外的 Profile 配置文件
4. 操作系统环境变量
5. Java 系统属性(System.getProperties())
6. JNDI 属性
7. ServletContext 初始化参数
8. ServletConfig 初始化参数
9. SPRING_APPLICATION_JSON 属性
10. 命令行参数
11. 测试属性(@SpringBootTest)
12. DevTools 全局设置
理解优先级:
# 命令行参数优先级最高
java -jar app.jar --server.port=9090
# 环境变量次之
SERVER_PORT=8081 java -jar app.jar
# 配置文件最低
# application.yml 中 server.port=8080
配置文件
配置文件位置
Spring Boot 会自动从以下位置加载配置文件(按优先级从高到低):
1. 当前目录的 /config 子目录
./config/application.yml
2. 当前目录
./application.yml
3. classpath 的 /config 包
classpath:/config/application.yml
4. classpath 根目录
classpath:/application.yml
配置文件格式
Spring Boot 支持两种配置文件格式:
properties 格式
# application.properties
server.port=8080
server.servlet.context-path=/api
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=root
logging.level.root=INFO
logging.level.com.example=DEBUG
YAML 格式(推荐)
# application.yml
server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: root
logging:
level:
root: INFO
com.example: DEBUG
YAML 格式优势:
| 特性 | 说明 |
|---|---|
| 层次结构 | 使用缩进表示层级关系,更直观 |
| 减少重复 | 避免重复的前缀 |
| 支持复杂结构 | 支持列表、Map 等复杂类型 |
| 可读性 | 配置更易读、易维护 |
如果同时存在 application.properties 和 application.yml,.properties 文件优先级更高。建议整个项目统一使用一种格式。
多文档配置
YAML 支持在一个文件中定义多个文档,使用 --- 分隔:
# 通用配置
server:
port: 8080
---
# 开发环境配置
spring:
config:
activate:
on-profile: dev
server:
port: 8081
---
# 生产环境配置
spring:
config:
activate:
on-profile: prod
server:
port: 80
Profile 环境配置
什么是 Profile?
Profile 是 Spring 提供的一种机制,用于根据不同的环境加载不同的配置。
创建 Profile 配置文件
按照 application-{profile}.yml 命名规则创建:
resources/
├── application.yml # 主配置
├── application-dev.yml # 开发环境
├── application-test.yml # 测试环境
└── application-prod.yml # 生产环境
各环境配置示例:
# application.yml(通用配置)
spring:
application:
name: myapp
myapp:
name: 默认应用
# application-dev.yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb_dev
username: root
password: root
logging:
level:
root: DEBUG
myapp:
name: 开发环境应用
# application-prod.yml
server:
port: 80
spring:
datasource:
url: jdbc:mysql://prod-db:3306/mydb
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
logging:
level:
root: WARN
myapp:
name: 生产环境应用
激活 Profile
方式一:配置文件
# application.yml
spring:
profiles:
active: dev
方式二:命令行参数
java -jar app.jar --spring.profiles.active=prod
方式三:环境变量
# Linux/macOS
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jar
# Windows
set SPRING_PROFILES_ACTIVE=prod
java -jar app.jar
方式四:IDE 配置
在 IntelliJ IDEA 中:
- 打开 Run/Debug Configurations
- 在 Environment variables 中添加
SPRING_PROFILES_ACTIVE=dev - 或在 VM options 中添加
-Dspring.profiles.active=dev
激活多个 Profile
spring:
profiles:
active: dev,mysql,redis
java -jar app.jar --spring.profiles.active=prod,mysql,redis
Profile 分组
Spring Boot 2.4+ 支持Profile 分组:
spring:
profiles:
group:
dev:
- dev-db
- dev-mq
prod:
- prod-db
- prod-mq
active: dev
@Profile 条件装配
使用 @Profile 注解控制 Bean 的加载:
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
return DataSourceBuilder.create()
.url("jdbc:mysql://localhost:3306/mydb_dev")
.username("root")
.password("root")
.build();
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
return DataSourceBuilder.create()
.url("jdbc:mysql://prod-db:3306/mydb")
.username("${DB_USERNAME}")
.password("${DB_PASSWORD}")
.build();
}
}
在 Service 层使用:
@Service
@Profile("dev")
public class DevEmailService implements EmailService {
// 开发环境的邮件服务(打印日志)
}
@Service
@Profile("prod")
public class ProdEmailService implements EmailService {
// 生产环境的邮件服务(真实发送)
}
配置属性绑定
@Value 注解
最简单的方式是使用 @Value 注解直接注入属性:
@Service
public class MyService {
@Value("${myapp.name}")
private String appName;
@Value("${myapp.version:1.0.0}")
private String version; // 默认值 1.0.0
@Value("${myapp.features}")
private List<String> features; // 支持列表类型
public void printInfo() {
System.out.println("应用名称: " + appName);
System.out.println("版本: " + version);
}
}
配置文件:
myapp:
name: 我的应用
version: 2.0.0
features:
- 功能1
- 功能2
- 功能3
@ConfigurationProperties 注解
对于复杂的配置,推荐使用 @ConfigurationProperties:
@Data
@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
private String name;
private String version;
private Boolean enabled = true;
private List<String> features = new ArrayList<>();
private Database database = new Database();
private Map<String, String> metadata = new HashMap<>();
@Data
public static class Database {
private String url;
private String username;
private String password;
private Integer poolSize = 10;
}
}
对应配置:
myapp:
name: 我的应用
version: 2.0.0
enabled: true
features:
- 功能1
- 功能2
database:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: root
pool-size: 20
metadata:
author: 张三
department: 技术部
使用配置类:
@Service
@RequiredArgsConstructor
public class MyService {
private final MyAppProperties properties;
public void printInfo() {
System.out.println("应用名称: " + properties.getName());
System.out.println("数据库URL: " + properties.getDatabase().getUrl());
}
}
启用配置属性扫描
方式一:使用 @EnableConfigurationProperties:
@Configuration
@EnableConfigurationProperties(MyAppProperties.class)
public class AppConfig {
}
方式二:在配置类上使用 @Component:
@Data
@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
// ...
}
方式三:使用 @ConfigurationPropertiesScan:
@SpringBootApplication
@ConfigurationPropertiesScan
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
宽松绑定
Spring Boot 支持多种属性命名格式:
| 配置文件格式 | 说明 |
|---|---|
myapp.database.url | 标准小写点分隔 |
myapp.database-url | 短横线分隔 |
myapp.databaseUrl | 驼峰命名 |
MYAPP_DATABASE_URL | 环境变量大写下划线 |
示例:
# 以下写法都能绑定到 database.url 属性
myapp:
database:
url: jdbc:mysql://localhost:3306/mydb
# 或者
database-url: jdbc:mysql://localhost:3306/mydb
# 环境变量方式
MYAPP_DATABASE_URL=jdbc:mysql://localhost:3306/mydb
配置验证
使用 JSR-303 注解验证配置:
@Data
@ConfigurationProperties(prefix = "myapp")
@Validated
public class MyAppProperties {
@NotBlank(message = "应用名称不能为空")
private String name;
@Pattern(regexp = "^\\d+\\.\\d+\\.\\d+$", message = "版本号格式不正确")
private String version;
@Min(value = 1, message = "端口不能小于1")
@Max(value = 65535, message = "端口不能大于65535")
private Integer port;
@Valid
private Database database = new Database();
@Data
public static class Database {
@NotBlank(message = "数据库URL不能为空")
private String url;
@NotBlank(message = "数据库用户名不能为空")
private String username;
}
}
添加验证依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
高级配置
配置属性占位符
在配置文件中引用其他属性:
app:
name: MyApp
version: 1.0.0
description: ${app.name} 是一个优秀的应用
author: ${AUTHOR:Unknown} # 使用环境变量,默认值 Unknown
随机值配置
Spring Boot 提供随机值生成器:
app:
secret: ${random.value} # 随机字符串
number: ${random.int} # 随机整数
big-number: ${random.long} # 随机长整数
uuid: ${random.uuid} # 随机UUID
port: ${random.int[10000,20000]} # 指定范围的随机整数
导入外部配置
使用 spring.config.import 导入其他配置文件:
# application.yml
spring:
config:
import:
- optional:file:./dev.properties # 可选导入
- classpath:additional-config.yml # 类路径导入
配置加密
对于敏感信息,可以使用 Jasypt 进行加密:
添加依赖:
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
配置加密属性:
# 加密后的密码
spring:
datasource:
password: ENC(加密后的字符串)
# 加密密钥(建议通过环境变量传入)
jasypt:
encryptor:
password: ${JASYPT_ENCRYPTOR_PASSWORD}
生成加密字符串:
@Test
void encryptPassword() {
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword("my-secret-key");
String encrypted = encryptor.encrypt("my-password");
System.out.println("加密后: ENC(" + encrypted + ")");
}
配置最佳实践
1. 合理分层配置
application.yml # 通用配置
├── 应用名称、版本
├── 公共配置
└── 默认值
application-dev.yml # 开发环境
├── 本地数据库配置
├── 调试开关
└── 详细日志
application-prod.yml # 生产环境
├── 生产数据库配置
├── 环境变量引用
└── 性能优化配置
2. 敏感信息处理
# 不推荐:硬编码密码
spring:
datasource:
password: root123
# 推荐:使用环境变量
spring:
datasource:
password: ${DB_PASSWORD}
# 推荐:使用配置中心
spring:
datasource:
password: ${vault.database.password}
3. 配置文档化
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
/**
* 应用名称,用于日志和监控标识
*/
private String name;
/**
* 数据库连接池大小,默认10
*/
private Integer poolSize = 10;
}
4. 配置变更通知
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties implements ConfigurationPropertiesRebindable {
@Bean
public ConfigurationPropertiesRebinder rebinder() {
return new ConfigurationPropertiesRebinder();
}
}
5. 配置值校验
@PostConstruct
public void validate() {
if (port < 1024 || port > 65535) {
throw new IllegalArgumentException("端口号必须在 1024-65535 之间");
}
}
配置文件示例
完整配置示例
# application.yml
spring:
application:
name: myapp
profiles:
active: dev
# 数据源配置
datasource:
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:mydb}
username: ${DB_USERNAME:root}
password: ${DB_PASSWORD:root}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
minimum-idle: 5
maximum-pool-size: 20
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 30000
# JPA 配置
jpa:
hibernate:
ddl-auto: update
show-sql: false
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQLDialect
# 缓存配置
cache:
type: redis
redis:
time-to-live: 600000
# Redis 配置
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
database: 0
# 服务器配置
server:
port: ${SERVER_PORT:8080}
servlet:
context-path: /api
error:
include-message: always
include-binding-errors: always
# 日志配置
logging:
level:
root: INFO
com.example: DEBUG
file:
name: logs/myapp.log
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# Actuator 配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
endpoint:
health:
show-details: when-authorized
# 自定义配置
myapp:
name: MyApp
version: 1.0.0
enabled: true
features:
- feature1
- feature2
security:
jwt:
secret: ${JWT_SECRET:default-secret-key}
expiration: 86400000
小结
本章我们学习了:
- 外部化配置:理解配置加载顺序和优先级
- 配置文件:properties 和 YAML 格式的使用
- Profile:多环境配置管理
- 属性绑定:@Value 和 @ConfigurationProperties
- 高级特性:占位符、随机值、导入配置、加密
- 最佳实践:配置分层、敏感信息处理、文档化
练习
- 创建一个包含 dev、test、prod 三个环境的配置文件
- 使用 @ConfigurationProperties 绑定复杂配置
- 使用环境变量覆盖配置文件中的属性
- 为配置属性添加验证注解
- 实现配置的加密存储