配置能放在哪些地方
Spring Boot 启动时会从多个来源加载配置,后加载的覆盖先加载的,优先级从高到低大致是:
| 优先级 | 来源 | 示例 |
|---|---|---|
| 1 | 命令行参数 | --server.port=9090 |
| 2 | Java 系统属性 | -Dserver.port=9090 |
| 3 | 操作系统环境变量 | SERVER_PORT=9090 |
| 4 | jar 外部的 application-{profile}.yml |
./config/application-prod.yml |
| 5 | jar 内部的 application-{profile}.yml |
classpath:/application-prod.yml |
| 6 | jar 外部的 application.yml |
./application.yml |
| 7 | jar 内部的 application.yml |
classpath:/application.yml |
这张表最实用的结论是:外部配置优先于 jar 内配置。所以部署时可以在 jar 同级的 config 目录放一份 application-prod.yml 覆盖端口、数据库密码,不用重新打包。
除了本地文件,配置还可以来自配置中心。2022 年常见的是 Nacos、Apollo,它们通过额外依赖或 bootstrap.yml 把远程配置加载进来,优先级通常高于本地文件。引入配置中心后,排查问题第一步是确认“最终生效的是哪一份”。
bootstrap.yml 在 Spring Cloud 项目里负责引导阶段的配置,比如配置中心地址、应用名;application.yml 才是业务配置。两者名字相近但加载时机不同,放错位置会出现“配置中心连上了,业务配置没拉下来”的情况。
多个 profile 可以同时激活,用逗号分隔,比如 dev,local,后激活的覆盖先激活的。但别把互斥的环境一起激活,dev 和 prod 同时打开,数据库、日志、开关全乱。
配置的优先级不是绝对的:@PropertySource、@TestPropertySource、随机值等都有各自的位置。拿不准时用 /actuator/env 看每个属性的来源,比背优先级表可靠。还可以用 --spring.config.location 显式指定配置文件,把加载范围缩小到唯一一个。
application.yml 与 profile
多环境最简单的方式是用 --- 在一个文件里分文档,或者拆成 application-dev.yml、application-prod.yml。Spring Boot 2.4 之后,激活 profile 的写法变成了 spring.config.activate.on-profile:
server:
port: 8080
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev
server:
port: 8081
---
spring:
config:
activate:
on-profile: prod
server:
port: 8082
启动时用 --spring.profiles.active=prod 覆盖。生产环境建议把数据库、Redis 的地址和密码放在 application-prod.yml,这个文件不进 Git,由部署环境单独提供。
@Value 与 @ConfigurationProperties
取单个配置用 @Value("${app.mail.host}"),简单直接,适合零散的一两个值;配置一多,字段分散在各个类里,改一个 key 就要全局搜索。
一组配置更适合 @ConfigurationProperties:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "app.mail")
public class MailProperties {
private String host;
private int port;
private String username;
// 省略 getter / setter
}
对应配置:
app:
mail:
host: smtp.example.com
port: 465
username: no-reply@example.com
它支持松散绑定:配置里写 smtp-host,类里写 smtpHost 也能注入;@Value 则要求 key 完全一致。需要校验时还可以在类上加 @Validated 和 @NotBlank。
命令行与环境变量覆盖
# 临时改端口
java -jar app.jar --server.port=9090
# 切换环境
java -jar app.jar --spring.profiles.active=prod
# 环境变量方式,点换成下划线并大写
export SERVER_PORT=9090
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jar
容器部署时环境变量方式最常用,Kubernetes 的 ConfigMap、Docker 的 -e 都是这个原理。
常见坑
1. YAML 格式写错
port:8080 少了冒号后的空格,YAML 会把它当成一个字符串值而不是键值对;缩进用了 Tab 也会报解析错误。写完在 IDEA 里看有没有黄色波浪线,YAML 插件能提前发现大部分问题。
2. profile 没激活
spring.profiles.active 拼成 spring.profile.active,或者写在 --- 分隔的第二个文档里(那时已经晚了)。启动日志里会打印 The following profiles are active: ...,先确认这一行。
3. @Value 写了不存在的 key
@Value("${app.mail.host}") 配错 key,启动直接报 Could not resolve placeholder,反而好查;但如果给了默认值 @Value("${app.mail.host:smtp.example.com}"),就会静默用默认值,线上发不出邮件才被发现。
4. 外部配置意外覆盖
在 jar 同级目录放了一份旧的 application.yml 或 config/application.yml,优先级高于 jar 内部,改了代码里的配置也不生效。排查时先看启动目录下有没有这些文件。
5. 密码里有特殊字符
YAML 里 # 表示注释,: 后跟空格会被当成键值对。密码含这些字符时用单引号包起来,或者改用环境变量注入。
排查步骤
- 启动日志里找
The following profiles are active,确认环境; - 引入
spring-boot-starter-actuator后访问/actuator/env,能看到每个配置项的来源; - 加
--debug启动,查看自动配置报告; - 怀疑外部配置覆盖时,检查 jar 同级目录和
config目录; - 用
--spring.config.location显式指定配置文件路径,排除加载顺序干扰。
评论