第 47 章:多环境配置
学习目标
- 掌握 properties 与 yml 的配置方式
- 理解配置优先级
- 学会多环境隔离与配置注入
一、yml vs properties
properties
# properties:扁平,重复前缀多
spring.datasource.url=jdbc:mysql://localhost:3306/taskflow
spring.datasource.username=root
spring.datasource.password=123456yaml
# yml:层级清晰 ✅ 推荐
spring:
datasource:
url: jdbc:mysql://localhost:3306/taskflow
username: root
password: 123456⚠️ yml 三个坑:
- 缩进只能用空格,Tab 会直接报错
- 冒号后必须有空格:
key: value(不是key:value)- 特殊字符(
:、@、*)开头的值要加引号
二、多环境配置
src/main/resources/
├── application.yml # 公共配置 + 激活哪个环境
├── application-dev.yml # 开发环境
├── application-test.yml # 测试环境
├── application-prod.yml # 生产环境
└── application-local.yml # 个人本地(加入 .gitignore)application.yml:
yaml
spring:
profiles:
active: @env@ # ① Maven profile 注入,或直接写 dev
application:
name: taskflow
# 所有环境共享的配置写这里
mybatis-plus:
configuration:
map-underscore-to-camel-case: trueapplication-dev.yml:
yaml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/taskflow_dev?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
logging:
level:
com.taskflow: debug # 开发环境打 SQLapplication-prod.yml:
yaml
server:
port: 8080
spring:
datasource:
url: ${DB_URL} # ① 生产环境从环境变量读,不写死
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
logging:
level:
root: warn
com.taskflow: info① 生产密码绝对不能进 Git。方案:环境变量、配置中心(Nacos/Apollo)、或 Jasypt 加密(见第 48 章)。
激活方式
bash
# ① 命令行(优先级最高,部署常用)
java -jar app.jar --spring.profiles.active=prod
# ② 环境变量
export SPRING_PROFILES_ACTIVE=prod
# ③ JVM 参数
java -Dspring.profiles.active=prod -jar app.jar
# ④ Maven 打包时确定
mvn clean package -Pprod多 profile 组合
yaml
spring:
profiles:
active: prod,monitor,swagger # 可以同时激活多个yaml
# 分组:激活 prod 时自动激活 db-prod 和 redis-prod
spring:
profiles:
group:
prod: db-prod,redis-prod,mq-prod三、配置优先级
高优先级覆盖低优先级。这意味着你可以打一个 jar 包,通过外部配置文件或命令行参数适配所有环境——一次构建,多处部署。
bash
# 外部配置文件(不用重新打包就能改配置)
java -jar app.jar --spring.config.location=file:/etc/taskflow/application.yml
# 追加外部配置(不覆盖 jar 内的,只做补充)
java -jar app.jar --spring.config.additional-location=file:/etc/taskflow/四、读取配置的三种方式
① @Value(单个值)
java
@Component
public class MyService {
@Value("${app.name}")
private String appName;
@Value("${app.timeout:5000}") // ① 冒号后是默认值
private int timeout;
@Value("${app.tags:a,b,c}") // ② 逗号分隔自动转 List
private List<String> tags;
@Value("#{${app.map}}") // ③ SpEL 解析 Map
private Map<String, String> map;
}⚠️
@Value不能用在static字段上;也不能在构造器里使用(此时还没注入)。
② @ConfigurationProperties(一组配置,推荐)
yaml
app:
oss:
endpoint: https://oss-cn-hangzhou.aliyuncs.com
bucket: taskflow-files
max-size: 10MB
allowed-types:
- jpg
- png
- pdfjava
@Data
@Component
@ConfigurationProperties(prefix = "app.oss") // ① 前缀绑定
@Validated // ② 支持校验,配置错了启动就失败
public class OssProperties {
@NotBlank
private String endpoint;
@NotBlank
private String bucket;
private DataSize maxSize = DataSize.ofMegabytes(10); // ③ 自动解析 10MB
private List<String> allowedTypes = new ArrayList<>();
}为什么推荐
@ConfigurationProperties?
- 一组相关配置聚合成对象,语义清晰
- 松散绑定:
max-size、maxSize、MAX_SIZE都能绑上- 支持 JSR-303 校验,配置写错启动时就报错,而不是运行到那行才炸
- IDE 有自动补全提示(加上 configuration-processor 依赖)
开启 IDE 提示:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>③ Environment(动态读取)
java
@Autowired
private Environment env;
String url = env.getProperty("spring.datasource.url");
int port = env.getProperty("server.port", Integer.class, 8080);
boolean isProd = env.acceptsProfiles(Profiles.of("prod"));五、条件化配置
java
@Configuration
public class CacheConfig {
@Bean
@ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")
public CacheManager redisCacheManager() { }
@Bean
@ConditionalOnMissingBean(CacheManager.class) // ① 没有其他实现时才生效
public CacheManager simpleCacheManager() { }
@Bean
@Profile("!prod") // ② 非生产环境才生效
public DataInitializer dataInitializer() { }
}| 注解 | 生效条件 |
|---|---|
@ConditionalOnProperty | 配置项满足条件 |
@ConditionalOnMissingBean | 容器中没有该类型 Bean |
@ConditionalOnBean | 容器中有该类型 Bean |
@ConditionalOnClass | classpath 中有该类 |
@Profile | 指定环境激活 |
这套注解就是 Spring Boot 自动配置的核心:
spring-boot-autoconfigure里全是@ConditionalOnXxx。比如你加了mysql-connector依赖,DataSourceAutoConfiguration的@ConditionalOnClass(DataSource.class)成立,就自动帮你配好数据源。
六、常用配置速查
yaml
server:
port: 8080
servlet:
context-path: /api # 统一前缀
tomcat:
threads:
max: 200 # 最大工作线程
min-spare: 10
max-connections: 8192
accept-count: 100 # 等待队列
connection-timeout: 20000
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null # null 字段不返回给前端
serialization:
write-dates-as-timestamps: false
fail-on-empty-beans: false
servlet:
multipart:
max-file-size: 10MB
max-request-size: 50MB
mvc:
throw-exception-if-no-handler-found: true # 404 也走全局异常处理
web:
resources:
add-mappings: false # 配合上面一条使用
logging:
level:
root: info
com.taskflow: debug
file:
name: logs/taskflow.log七、配置中心(进阶)
单机配置文件的问题:改配置要重启、多实例要逐个改、没有变更审计。
| 方案 | 特点 |
|---|---|
| Nacos | 阿里开源,配置 + 注册中心二合一,国内最流行 |
| Apollo | 携程开源,权限/灰度/审计最完善 |
| Spring Cloud Config | 官方方案,基于 Git,功能较简单 |
yaml
# Nacos 示例
spring:
cloud:
nacos:
config:
server-addr: nacos.example.com:8848
file-extension: yml
namespace: prodjava
@RefreshScope // ① 配置变更时自动刷新这个 Bean
@Component
public class MyService {
@Value("${app.feature.enabled}")
private boolean enabled;
}八、本章小结
| 要点 | 关键 |
|---|---|
| yml | 缩进用空格,冒号后有空格 |
| 多环境 | application-{env}.yml |
| 激活 | --spring.profiles.active=prod |
| 优先级 | 命令行 > 环境变量 > 外部文件 > 内部文件 |
| 读配置 | 一组用 @ConfigurationProperties |
| 配置校验 | @Validated,启动即失败 |
| 生产密码 | 环境变量 / 配置中心,绝不进 Git |
| 条件装配 | @ConditionalOnXxx 是自动配置的基础 |
动手练习
练习 1:基础题
创建 dev / prod 两个环境配置,端口分别是 8080 / 9090,用 --spring.profiles.active 切换验证。
练习 2:进阶题
定义一个 @ConfigurationProperties 类接收短信配置(appKey、appSecret、模板 ID 列表),加上 @Validated 校验,故意填错配置验证启动失败。
下一章:第 48 章:Jasypt 配置加密 →