跳到主要内容

配置管理

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.propertiesapplication.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 中:

  1. 打开 Run/Debug Configurations
  2. 在 Environment variables 中添加 SPRING_PROFILES_ACTIVE=dev
  3. 或在 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

小结

本章我们学习了:

  1. 外部化配置:理解配置加载顺序和优先级
  2. 配置文件:properties 和 YAML 格式的使用
  3. Profile:多环境配置管理
  4. 属性绑定:@Value 和 @ConfigurationProperties
  5. 高级特性:占位符、随机值、导入配置、加密
  6. 最佳实践:配置分层、敏感信息处理、文档化

练习

  1. 创建一个包含 dev、test、prod 三个环境的配置文件
  2. 使用 @ConfigurationProperties 绑定复杂配置
  3. 使用环境变量覆盖配置文件中的属性
  4. 为配置属性添加验证注解
  5. 实现配置的加密存储

参考资源