大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
配置与 Profile
这一课你将能做到
- 用 yml 组织项目配置,并用
@Value和@ConfigurationProperties两种方式注入到代码 - 建立 dev / prod 多环境配置,用三种方式(yml、命令行、环境变量)切换 Profile
- 说清 Spring Boot 配置的优先级顺序,并用环境变量管理数据库密码等敏感信息
为什么要学
同一套代码,本地连 H2、测试连测试库、生产连生产库,端口、日志级别、第三方密钥全都不一样。配置与代码分离是后端工程的基本素养:代码管「怎么做」,配置管「用什么参数做」。
人话:application.yml 就是应用的「控制面板」。本站后端的数据库地址、Redis 密码、JWT 密钥全在这里管理,绝没有写死在代码里。这一课学的每个细节,上生产第一天就会用到。
先建立图像
application.yml ← 所有环境共用的配置(应用名、通用行为)
application-dev.yml ← 开发环境:H2 内存库、SQL 打印、日志 DEBUG
application-prod.yml ← 生产环境:MySQL、日志 INFO、敏感信息用环境变量
↑
启动时通过 spring.profiles.active 指定激活哪个(可同时激活多个,后者覆盖前者)
优先级(高 → 低,记住前四层够用):
命令行参数 > 环境变量 > application-{profile}.yml > application.yml
规则一句话:越「靠近启动动作」的配置越优先,Profile 专属配置覆盖通用配置。
跟着做
步骤 1:写一份规范的 application.yml
覆盖 src/main/resources/application.yml:
spring:
application:
name: taskflow
server:
port: 8080
# 自定义业务配置(约定以应用名开头,避免与框架键冲突)
taskflow:
welcome: "欢迎来到任务管理平台"
upload-dir: ./data/upload
white-list:
- 192.168.1.10
- 10.0.0.5
yml 两条铁律:缩进用空格不用 Tab;同级键对齐,层级靠缩进表达。- 开头的行是列表元素。
步骤 2:@Value 读取(零散配置用)
写个临时 Controller 验证读取:
package com.example.taskflow.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class ConfigDemoController {
@Value("${taskflow.welcome}")
private String welcome;
@Value("${taskflow.upload-dir}")
private String uploadDir;
@GetMapping("/api/config-demo")
public String demo() {
return welcome + " | 上传目录: " + uploadDir;
}
}
启动后访问 http://localhost:8080/api/config-demo 即可看到注入结果。${taskflow.welcome:默认值} 的冒号写法还能提供兜底默认值。
步骤 3:@ConfigurationProperties 读取(成组配置用,推荐)
零散值用 @Value,一组相关配置用 @ConfigurationProperties 绑成对象。新建 config/TaskflowProperties.java:
package com.example.taskflow.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@ConfigurationProperties(prefix = "taskflow")
public record TaskflowProperties(
String welcome,
String uploadDir,
List<String> whiteList
) {}
启动类加上扫描(或直接加在启动类上):
@SpringBootApplication
@ConfigurationPropertiesScan // 让 @ConfigurationProperties 类生效
public class TaskflowApplication {
public static void main(String[] args) {
SpringApplication.run(TaskflowApplication.class, args);
}
}
在任意 Bean 中注入使用:
@RestController
public class ConfigDemoController {
private final TaskflowProperties properties;
public ConfigDemoController(TaskflowProperties properties) {
this.properties = properties;
}
@GetMapping("/api/config-props")
public List<String> whiteList() {
return properties.whiteList();
}
}
注意:yml 里的 upload-dir(中划线)自动绑定到 uploadDir(驼峰),这叫松散绑定,record 的构造器绑定由 Boot 自动完成,无需任何 setter。
步骤 4:建立 dev / prod 双环境
新建 application-dev.yml(开发环境):
spring:
datasource:
url: jdbc:h2:mem:taskflow;DB_CLOSE_DELAY=-1
driver-class-name: org.h2.Driver
username: sa
password: ""
logging:
level:
com.example.taskflow: DEBUG
org.springframework.jdbc: DEBUG
新建 application-prod.yml(生产环境):
spring:
datasource:
url: jdbc:mysql://${DB_HOST:localhost}:3306/taskflow
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${DB_USER:taskflow}
password: ${DB_PASSWORD} # 只从环境变量读,不写默认值
hikari:
maximum-pool-size: 20
logging:
level:
com.example.taskflow: INFO
在 application.yml 里指定默认激活 dev:
spring:
profiles:
active: dev
${DB_PASSWORD} 是占位符:运行时从环境变量取值。配合 export DB_PASSWORD=xxx(Linux)或 IDEA Run Configuration 的 Environment variables(Windows)注入。数据库密码出现在代码仓库里是安全事故级别的问题。
步骤 5:三种方式切换 Profile
方式一:改 application.yml 的 spring.profiles.active(适合本地)。
方式二:命令行参数(优先级最高,适合启动脚本):
java -jar taskflow.jar --spring.profiles.active=prod
方式三:环境变量(容器/云环境标准做法):
# Linux / macOS
export SPRING_PROFILES_ACTIVE=prod
java -jar taskflow.jar
Windows PowerShell 对应 $env:SPRING_PROFILES_ACTIVE="prod"。启动日志第一行附近会打印 The following 1 profile is active: "prod",部署后先看这行确认环境没切错。
步骤 6:动手验证优先级
保持 dev 激活,用命令行覆盖端口启动:
mvnw spring-boot:run -Dspring-boot.run.arguments=--server.port=9090
日志会显示 Tomcat started on port 9090——yml 里的 8080 被命令行覆盖了。这就是「命令行 > yml」的直观证明。生产上紧急改个配置不用重新打包,加参数重启即可(但改完要同步回配置文件,否则下次重启回退)。
讲清楚要点
- @Value 与 @ConfigurationProperties 的分工:前者适合一两个零散值;后者把成组配置绑成类型安全对象,支持校验、IDE 提示和松散绑定,团队项目默认用它。
- Profile 是「配置切片」不是「代码分支」:
application-{profile}.yml只负责差异部分,公共配置留在主文件,避免两边各维护一份全量。 - 敏感信息永远走环境变量或配置中心:yml 里只出现
${DB_PASSWORD}占位符。真实企业里再进一步用 Nacos/Vault 这类配置中心集中管理与加密。 - 优先级要理解而不是死背:命令行和环境变量是「启动时的临时输入」,当然优先于「打进 jar 里的静态文件」;profile 文件比主文件更具体,覆盖也更合理。
- 改配置 = 改参数 ≠ 改代码:凡是发现自己在代码里写
if (环境 == 生产),都是配置设计错了,把它变成配置项。
你可能会懵的地方
-
yml 和 properties 能混用吗? 能,Spring 会同时加载同名两种格式,yml 优先。但一个项目只统一用一种,混着写迟早有人改错文件。
-
多模块项目 / 打包后,配置文件从哪加载? 优先级从高到低:jar 外的
config/目录 → jar 外根目录 → jar 内 classpath 的config/→ jar 内 classpath 根。所以生产上经常把application-prod.yml放在 jar 同级的config/目录做「不改包覆盖」。 -
同一个 Profile 激活多个时(如 dev,local)哪个赢? 后声明的赢:
spring.profiles.active=dev,local中 local 的配置覆盖 dev 的同名键。利用这一点可以做「环境基础 + 个人本地微调」的组合。 -
@Value 注入的时机为什么比构造器执行早/晚出问题? @Value 是字段注入,发生在 Bean 实例化之后。若在构造器里读这个字段还是 null——要么改用 @ConfigurationProperties + 构造器注入,要么把 @Value 挪到构造器参数上。
-
配置改了必须重启吗? 默认是。动态刷新需要配置中心(Nacos 等)配合
@RefreshScope,属于进阶话题;本阶段建立「改配置 → 重启 → 验证」的肌肉记忆即可。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| yml 缩进用 Tab | 解析直接报错 while scanning ... found character '\t' | 全部用空格,IDE 统一格式化 |
| 数据库密码明文写进 application-prod.yml 并提交 | 密码泄露进 git 历史 | 用 ${DB_PASSWORD} + 环境变量注入 |
| 每个环境复制一份全量 application.yml | 改一个公共配置要改 N 处 | 主文件放公共,profile 文件只放差异 |
| 生产验证只看页面不查启动日志 | Profile 切错连错库而不自知 | 启动后确认 active profile 日志行 |
| @Value 写错 key(如 taskflow.welcom) | 启动即抛 Could not resolve placeholder | 保持 key 拼写一致,或加 :默认值 兜底 |
卡点急救
- 启动报
Could not resolve placeholder 'xxx':yml 里没有这个 key 或拼写不一致。检查 yml 层级缩进,@Value的 key 与 yml 完全一致;临时可用${xxx:默认值}兜底。 - yml 报
ScannerException: mapping values are not allowed here:几乎都是缩进错位或冒号后少了空格。yml 键后必须空格冒号空格值(key: value)。 - Profile 切了但配置像没生效:先看启动日志
The following profiles are active: ...确认激活成功;再确认没有命令行参数或环境变量把同名键又覆盖回去了(它们优先级更高)。 - 环境变量注入后仍拿到 null:Windows 下检查变量名大小写与作用域(用户级/系统级需重开终端生效);IDEA 里确认配置在 Run/Debug Configuration 的 Environment variables 中。
面试常问
-
Spring Boot 配置文件的加载优先级? 至少说出:命令行参数 > 系统环境变量 > jar 外 config/ 目录 > jar 外根目录 > jar 内 config/ > jar 内 classpath;同层内
application-{profile}.yml覆盖application.yml。 -
@Value 和 @ConfigurationProperties 怎么选? 零散单值用 @Value;一组相关配置用 @ConfigurationProperties:类型安全、松散绑定、可配合 JSR-303 校验、支持 IDE 元数据提示,利于维护。
-
多环境配置怎么实现?生产敏感信息怎么管? Profile 切片(application-{profile}.yml)+
spring.profiles.active激活;敏感信息用环境变量占位符注入,进一步用配置中心(Nacos/Apollo/Vault)集中管理与加密。 -
Bootstrap 配置和 Application 配置的区别? 引入 Spring Cloud 后,
bootstrap.yml先于application.yml加载,用于存放连接配置中心本身的地址;纯 Boot 项目没有这个概念。
自测三问
- 命令行参数、环境变量、application-prod.yml、application.yml 四者的优先级从高到低怎么排?
taskflow.upload-dir能绑定到 record 组件uploadDir吗?这叫什么机制?- 生产环境的数据库密码应该怎么注入?写出 yml 中的占位符写法。
小结一句话
配置与代码分离、Profile 按环境切片、敏感信息走环境变量——把「控制面板」管好,应用才能一套代码跑遍所有环境。
延伸
- Spring Boot 官方文档:Externalized Configuration
- Spring Boot 官方文档:Profiles
- 下一课:IoC 与依赖注入——把「对象自己 new」变成「容器送上门」。