一、配置管理概述
1. 为什么需要配置管理?
| 问题 | 影响 |
|---|---|
| 硬编码配置 | 修改配置需要重新编译部署 |
| 环境差异 | 开发/测试/生产环境配置混乱 |
| 敏感信息泄露 | 密码、密钥暴露在代码中 |
| 配置分散 | 难以统一管理和维护 |
2. 配置管理的好处
| 好处 | 说明 |
|---|---|
| 环境隔离 | 不同环境使用不同配置 |
| 外部化配置 | 配置与代码分离 |
| 热更新 | 无需重启即可更新配置 |
| 版本控制 | 配置文件可以纳入版本管理 |
二、配置文件
1. 配置文件优先级
Spring Boot 按照以下顺序加载配置文件(优先级从高到低):
| 优先级 | 位置 | 说明 |
|---|---|---|
| 1 | 命令行参数 | --server.port=8081 |
| 2 | 环境变量 | SERVER_PORT=8081 |
| 3 | 外部配置文件 | ./config/application.properties |
| 4 | 外部配置文件 | ./application.properties |
| 5 | 内部配置文件 | classpath:/config/application.properties |
| 6 | 内部配置文件 | classpath:/application.properties |
| 7 | 默认配置 | Spring Boot 默认配置 |
2. 配置文件格式
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
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# 应用配置
app.name=My Application
app.version=1.0.0
application.yml
server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: root
jpa:
hibernate:
ddl-auto: update
show-sql: true
app:
name: My Application
version: 1.0.0
application.yaml
# application.yaml 与 application.yml 功能相同
# yaml 和 yml 是同一格式的不同扩展名
server:
port: 8080
3. 文件格式对比
| 特性 | .properties | .yml/.yaml |
|---|---|---|
| 层级关系 | 使用点号分隔 | 使用缩进表示 |
| 可读性 | 较差 | 较好 |
| 表达式 | 支持 SpEL | 支持 SpEL |
| 复杂结构 | 需要索引 | 原生支持 |
| 推荐场景 | 简单配置 | 复杂配置 |
三、环境配置
1. Profile 机制
创建不同环境的配置文件
src/main/resources/
├── application.properties # 默认配置
├── application-dev.properties # 开发环境
├── application-test.properties # 测试环境
└── application-prod.properties # 生产环境
application-dev.properties
# 开发环境配置
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/dev_db
spring.datasource.username=root
spring.datasource.password=dev_pass
logging.level.com.example.myapp=DEBUG
application-test.properties
# 测试环境配置
server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/test_db
spring.datasource.username=root
spring.datasource.password=test_pass
logging.level.com.example.myapp=INFO
application-prod.properties
# 生产环境配置
server.port=443
spring.datasource.url=jdbc:mysql://prod-server:3306/prod_db
spring.datasource.username=prod_user
spring.datasource.password=${DB_PASSWORD}
logging.level.com.example.myapp=WARN
logging.file.name=/var/log/myapp/app.log
2. 激活 Profile
方式一:配置文件
# application.properties
spring.profiles.active=dev
方式二:命令行参数
java -jar myapp.jar --spring.profiles.active=prod
方式三:环境变量
export SPRING_PROFILES_ACTIVE=prod
java -jar myapp.jar
方式四:启动类
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
app.setAdditionalProfiles("dev"); // 设置激活的 Profile
app.run(args);
}
}
3. 配置文件继承
application.properties(基础配置)
↓
application-{profile}.properties(环境特定配置)
↓
最终配置(合并)
示例:
application.properties:
app.name=My Application
app.version=1.0.0
application-dev.properties:
app.environment=Development
logging.level=DEBUG
最终配置(dev 环境):
app.name=My Application
app.version=1.0.0
app.environment=Development
logging.level=DEBUG
四、配置读取
1. @Value 注解
基本用法
@Component
public class MyComponent {
@Value("${server.port}")
private int serverPort;
@Value("${app.name}")
private String appName;
@Value("${app.version}")
private String appVersion;
public void printConfig() {
System.out.println("Server Port: " + serverPort);
System.out.println("App Name: " + appName);
System.out.println("App Version: " + appVersion);
}
}
默认值
@Component
public class MyComponent {
@Value("${app.timeout:3000}") // 默认值 3000
private int timeout;
@Value("${app.description:无描述}") // 默认值 "无描述"
private String description;
@Value("${app.enabled:true}") // 默认值 true
private boolean enabled;
}
SpEL 表达式
@Component
public class MyComponent {
// 计算表达式
@Value("#{10 * 20}")
private int result;
// 读取系统属性
@Value("#{systemProperties['user.home']}")
private String userHome;
// 读取环境变量
@Value("#{systemEnvironment['JAVA_HOME']}")
private String javaHome;
// 条件判断
@Value("#{app.timeout > 5000 ? 'Long' : 'Short'}")
private String timeoutType;
}
2. @ConfigurationProperties
创建配置类
package com.example.myapp.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String version;
private String description;
private Database database = new Database();
private Cache cache = new Cache();
private Feature feature = new Feature();
@Data
public static class Database {
private String url;
private String username;
private String password;
private int maxConnections = 10;
}
@Data
public static class Cache {
private boolean enabled = true;
private String type = "redis";
private long ttl = 3600;
}
@Data
public static class Feature {
private boolean newUserRegistration = true;
private boolean emailVerification = false;
private boolean socialLogin = true;
}
}
配置文件
application.yml:
app:
name: My Application
version: 1.0.0
description: Spring Boot Demo Application
database:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: root
max-connections: 20
cache:
enabled: true
type: redis
ttl: 7200
feature:
new-user-registration: true
email-verification: true
social-login: false
使用配置
@Service
public class MyService {
@Autowired
private AppProperties appProperties;
public void printConfig() {
System.out.println("App Name: " + appProperties.getName());
System.out.println("Version: " + appProperties.getVersion());
System.out.println("DB URL: " + appProperties.getDatabase().getUrl());
System.out.println("Cache Enabled: " + appProperties.getCache().isEnabled());
System.out.println("Features: " + appProperties.getFeature());
}
}
3. 配置验证
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
验证配置
@Data
@Component
@ConfigurationProperties(prefix = "app")
@Validated // 启用验证
public class AppProperties {
@NotBlank(message = "应用名称不能为空")
private String name;
@Pattern(regexp = "\\d+\\.\\d+\\.\\d+", message = "版本号格式不正确")
private String version;
@Min(value = 1000, message = "超时时间不能小于 1000ms")
private int timeout;
@Valid
private Database database = new Database();
@Data
public static class Database {
@NotBlank(message = "数据库 URL 不能为空")
private String url;
@Min(value = 1, message = "最大连接数不能小于 1")
@Max(value = 100, message = "最大连接数不能大于 100")
private int maxConnections = 10;
}
}
五、环境变量
1. 环境变量命名规则
| 配置属性 | 环境变量 |
|---|---|
server.port | SERVER_PORT |
spring.datasource.url | SPRING_DATASOURCE_URL |
app.database.username | APP_DATABASE_USERNAME |
2. 使用环境变量
方式一:直接设置
export SERVER_PORT=8081
export SPRING_DATASOURCE_URL=jdbc:mysql://localhost:3306/mydb
export SPRING_DATASOURCE_USERNAME=root
export SPRING_DATASOURCE_PASSWORD=root
java -jar myapp.jar
方式二:Docker
FROM openjdk:17-jdk-slim
ENV SERVER_PORT=8080
ENV SPRING_PROFILES_ACTIVE=prod
ENV DB_PASSWORD=secure_password
COPY target/myapp.jar /app/myapp.jar
EXPOSE 8080
CMD ["java", "-jar", "/app/myapp.jar"]
方式三:Docker Compose
version: '3.8'
services:
myapp:
image: myapp:latest
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=root
depends_on:
- mysql
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=mydb
六、配置加密
1. Jasypt 加密
添加依赖
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
配置 Jasypt
application.yml:
jasypt:
encryptor:
password: my-secret-key # 加密密钥(应该从环境变量获取)
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
spring:
datasource:
password: ENC(encrypted_password_here)
加密密码
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
public class PasswordEncryptor {
public static void main(String[] args) {
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword("my-secret-key"); // 加密密钥
encryptor.setAlgorithm("PBEWithMD5AndDES");
String plainPassword = "root";
String encryptedPassword = encryptor.encrypt(plainPassword);
System.out.println("Plain: " + plainPassword);
System.out.println("Encrypted: " + encryptedPassword);
}
}
从环境变量读取密钥
export JASYPT_ENCRYPTOR_PASSWORD=production-secret-key
java -jar myapp.jar
2. 云配置中心
Spring Cloud Config
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
配置服务端:
@SpringBootApplication
@EnableConfigServer // 启用配置服务器
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
配置文件:
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: https://github.com/your-repo/config-repo
default-label: main
客户端配置:
spring:
cloud:
config:
uri: http://localhost:8888
name: myapp
profile: prod
七、配置刷新
1. Actuator 配置刷新
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
启用刷新端点
application.yml:
management:
endpoints:
web:
exposure:
include: refresh,health,info
标记配置为可刷新
@Component
@RefreshScope // 标记为可刷新
@ConfigurationProperties(prefix = "app")
public class AppProperties {
// 配置属性
}
刷新配置
curl -X POST http://localhost:8080/actuator/refresh
2. 配置监听
@Component
public class ConfigChangeListener {
@EventListener
public void onEnvironmentChangeEvent(
EnvironmentChangeEvent event
) {
System.out.println("配置已更新:");
event.getKeys().forEach(key -> {
System.out.println(" - " + key);
});
}
}
八、最佳实践
1. 配置管理最佳实践
| 实践 | 说明 |
|---|---|
| 外部化配置 | 配置与代码分离 |
| 环境隔离 | 使用 Profile 区分环境 |
| 敏感信息加密 | 使用 Jasypt 或密钥管理服务 |
| 使用配置类 | @ConfigurationProperties 优于 @Value |
| 配置验证 | 使用 @Validated 验证配置 |
| 文档化配置 | 为配置提供说明文档 |
2. 安全性最佳实践
| 实践 | 说明 |
|---|---|
| 不在代码中硬编码密码 | 使用环境变量或密钥管理 |
| 加密敏感信息 | 使用 Jasypt 或云密钥服务 |
| 限制配置访问 | 使用安全配置中心 |
| 定期轮换密钥 | 定期更新加密密钥 |
| 最小权限原则 | 应用只访问必要的配置 |
九、总结
| 概念 | 说明 |
|---|---|
| 配置文件 | properties 和 yml 格式 |
| Profile | 环境隔离机制 |
| @Value | 简单配置注入 |
| @ConfigurationProperties | 类型安全配置绑定 |
| 环境变量 | 外部配置源 |
| 配置加密 | Jasypt 加密敏感信息 |
| 配置刷新 | 热更新配置 |