难道你不想把 Spring 的配置管理玩得又稳又清爽吗?
开篇语
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
前言 🤔
— 配置管理听起来枯燥,但项目中一旦没有把配置层设计好,污点会在运维/发版/本地调试里天天打卡出现。下面这份实务指南,把 application.properties / application.yml、@ConfigurationProperties、@Value、Profiles(spring.profiles.active)以及配置优先级、外部化、校验、常见陷阱和实战演练一次讲透——带代码、带命令、带坑位与解决方案。文字尽量通俗、有感情(会偶尔抱怨配置冲突那点小悲伤 😅),但技术点严谨可用。开始吧!
1. 配置的基本概念与选择
-
Properties (.properties):键值对,简单、直观,适合小量配置。
-
YAML (.yml / .yaml):支持层级、列表、映射,视觉更清晰,适合结构化配置。
-
注解绑定:
@Value("${some.prop:default}"):快速读取单值,适合少量简单配置或在@Component中注入。@ConfigurationProperties(prefix="app"):绑定整个对象(POJO),适合复杂、嵌套或复用的配置;便于测试与校验。
小贴士:如果配置项很多、带嵌套(比如
app.security.oauth),请优先用@ConfigurationProperties—— 可读性、单元测试和校验都更友好。
2. @ConfigurationProperties 绑定与校验(@Validated)——示例讲解
2.1 依赖(Spring Boot 项目常见)
使用 Spring Boot(2.4+ / 3.x 均类似)时只需引入 spring-boot-starter(通常 spring-boot-starter、spring-boot-starter-validation 如果你要用 JSR-303 校验)。
示例 build.gradle(核心):
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
2.2 POJO 示例(可绑定嵌套结构、支持校验)
// AppProperties.java
package com.example.config;
import jakarta.validation.constraints.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import java.time.Duration;
import java.util.List;
@Validated
@ConfigurationProperties(prefix = "app")
public class AppProperties {
@NotBlank
private String name;
@Min(1)
private int maxUsers = 100;
private Security security = new Security();
private List<String> tags;
// getters & setters
public static class Security {
@NotBlank
private String jwtSecret;
@NotNull
private Duration tokenTtl = Duration.ofHours(1);
// getters & setters
}
}
解析:
@ConfigurationProperties(prefix = "app")绑定app.*下的属性到AppProperties。@Validated+ JSR-303 注解(@NotBlank/@Min/@NotNull)将在容器启动时进行校验,校验失败会抛出启动异常(Fail fast),便于早发现配置问题。
2.3 注册方式(两种常见)
方式 A:在配置类上使用 @Component
@Component
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties { ... }
方式 B(更推荐于现代应用):@EnableConfigurationProperties 或 @ConfigurationPropertiesScan
// 在主类或配置类里
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
// 或者使用 @ConfigurationPropertiesScan 在包下扫描所有 @ConfigurationProperties
public class Application { }
注意:如果使用构造器绑定(immutable style),要用 @ConstructorBinding(并在 Spring Boot 2.2+ 或使用 @ConfigurationPropertiesScan 后支持)。示例:
@ConstructorBinding
@ConfigurationProperties("app")
public class AppProperties {
private final String name;
public AppProperties(String name) { this.name = name; }
public String getName() { return name; }
}
2.4 在 Controller 中使用(展示)
@RestController
@RequiredArgsConstructor
public class DemoController {
private final AppProperties appProperties;
@GetMapping("/config")
public Map<String, Object> config() {
return Map.of(
"name", appProperties.getName(),
"maxUsers", appProperties.getMaxUsers(),
"jwtTtl", appProperties.getSecurity().getTokenTtl()
);
}
}
3. 配置文件分层(默认、profile-specific、外部化)
Spring Boot 非常强调“外部化配置”(externalized configuration),以便在不同环境(dev / test / prod)使用不同值。
常见文件位置(从外到内):
./config/目录下的application.yml/application-{profile}.yml(项目根目录的 config 子目录)- 当前工作目录下的
application.yml - classpath(jar 包内)的
application.yml(src/main/resources)
优点:放在 ./config 更先被读取,方便运维在包外覆盖。
YAML 示例(含 profile 折叠写法)
application.yml(默认 + profile section)
spring:
application:
name: my-app
spring:
profiles: dev
app:
name: my-app-dev
maxUsers: 50
security:
jwtSecret: dev-secret
tokenTtl: PT1H
spring:
profiles: prod
app:
name: my-app
maxUsers: 1000
security:
jwtSecret: ${JWT_SECRET} # 生产从环境变量注入
tokenTtl: PT12H
或者更常见的做法:把 application-dev.yml 与 application-prod.yml 放在同路径,每个文件只包含对应 profile 的覆盖项。
4. 配置优先级规则(从高到低)
理解谁覆盖谁非常重要。下面给出简化且实用的优先级(从高到低)——在排查“为什么我的配置没有生效”时,这个列表会救你:
- 命令行参数(
--server.port=8081、--spring.profiles.active=prod) SPRING_APPLICATION_JSON环境变量(通过 JSON 设置属性)- Java 系统属性(
-D参数) - 操作系统环境变量(
SPRING_PROFILES_ACTIVE=prod或APP_SECURITY_JWTSECRET=xxx) - 外部配置文件(
/config/application-{profile}.yml或./application-{profile}.yml)——优先外部config/目录 - 打包内的配置文件(
classpath:/application-{profile}.yml) @PropertySource注解加载的资源(低优先级)application.properties/application.yml的默认值(SpringApplication.setDefaultProperties)
实用举例:如果你在
application.yml里写了app.name: abc,但通过命令行--app.name=override启动,则命令行值生效。
5. 实战练习:实现 dev / prod 两套配置并切换
下面给出一个最小可运行的 Spring Boot 示例,演示如何切换、如何通过环境变量与 Docker 启动不同 profile。
5.1 项目结构(简化)
demo-config/
├─ src/main/java/com/example/demo/Application.java
├─ src/main/java/com/example/demo/config/AppProperties.java
├─ src/main/java/com/example/demo/web/DemoController.java
├─ src/main/resources/
│ ├─ application.yml
│ ├─ application-dev.yml
│ └─ application-prod.yml
└─ build.gradle
5.2 关键文件内容(示例)
application.yml(基础):
spring:
profiles:
active: dev # 本地默认,可以在生产环境不使用此默认
app:
name: demo-default
maxUsers: 100
application-dev.yml:
app:
name: demo-dev
maxUsers: 20
security:
jwtSecret: dev-secret
tokenTtl: PT1H
application-prod.yml:
app:
name: demo-prod
maxUsers: 1000
security:
jwtSecret: ${JWT_SECRET} # 生产必须从环境变量注入
tokenTtl: PT12H
AppProperties.java(前面示例的简化版):
package com.example.demo.config;
import jakarta.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {
@NotBlank
private String name;
private int maxUsers;
private Security security = new Security();
// getters & setters...
public static class Security {
@NotBlank
private String jwtSecret;
// getters & setters
}
}
Application.java:
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
DemoController.java:
@RestController
@RequiredArgsConstructor
public class DemoController {
private final AppProperties props;
@Value("${spring.profiles.active:unknown}")
private String active;
@GetMapping("/info")
public Map<String, Object> info() {
return Map.of(
"profile", active,
"appName", props.getName(),
"maxUsers", props.getMaxUsers()
);
}
}
5.3 本地运行(常用切换方法)
-
用命令行切换 profile:
# 启动 prod profile(覆盖 application.yml 中的默认) ./gradlew bootRun --args='--spring.profiles.active=prod' # 或者 java -jar target/demo.jar --spring.profiles.active=prod -
用环境变量切换:
export SPRING_PROFILES_ACTIVE=prod export JWT_SECRET='super-secret-from-env' ./gradlew bootRun -
在 IDE 中添加 VM 参数:
-Dspring.profiles.active=prod(或在 Run configuration 添加 Program arguments--spring.profiles.active=prod)
5.4 Docker / docker-compose 示例(生产演练)
Dockerfile(极简):
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY build/libs/demo.jar /app/demo.jar
ENTRYPOINT ["java", "-jar", "/app/demo.jar"]
docker-compose.yml:
version: '3.8'
services:
app:
build: .
environment:
- SPRING_PROFILES_ACTIVE=prod
- JWT_SECRET=${JWT_SECRET}
ports:
- "8080:8080"
启动:
export JWT_SECRET='prod-secret-xxx'
docker-compose up --build
# curl http://localhost:8080/info
结果你会看到 /info 返回 profile: prod 与 appName: demo-prod。
6. 常见陷阱与排查(抓住这些就能省很多时间)
-
绑定失败 / 字段为 null
- 原因:没有
@EnableConfigurationProperties或没有把@ConfigurationProperties的类注册为 bean(@Component)或者没有@ConfigurationPropertiesScan。 - 解决:确保类被 Spring 扫描或在主类用
@EnableConfigurationProperties注册。
- 原因:没有
-
类型转换异常(Duration、DataSize、List 等)
- YAML 中
PT1H(ISO-8601)可绑定到Duration;也可写1h(部分版本)。若报错,检查值格式并尝试使用字符串或标准格式。 DataSize示例:10MB。
- YAML 中
-
@Value与@ConfigurationProperties的冲突@Value是即时解析的,@ConfigurationProperties更适配复杂对象。不要混用来负责同一配置源导致混乱。
-
构造器绑定(immutable)忘记添加
@ConstructorBinding- 如果使用构造器注入属性,必须标注
@ConstructorBinding或使用@ConfigurationPropertiesScan+ Spring Boot 版本支持,否则会报错或无法注入。
- 如果使用构造器注入属性,必须标注
-
环境变量命名映射问题
- Spring 的 “relaxed binding”:
app.security.jwt-secret-> 环境变量APP_SECURITY_JWT_SECRET(全部大写,下划线)。 - 在 Windows、Docker 等环境下注意不要出错。
- Spring 的 “relaxed binding”:
-
配置没覆盖:检查真正被加载的配置文件位置
- 在日志中(Spring Boot 启动时会打印
Mapped "{...}" properties from ...的信息)或启用调试--debug,可以看到属性来源(非常有用)。
- 在日志中(Spring Boot 启动时会打印
-
敏感信息(secret)错误泄露
- 切记:把
JWT_SECRET写进application.yml(包内)会被打包进 jar;生产应通过环境变量或 secret 管理器(K8s Secret / Vault / cloud secret)注入。
- 切记:把
7. 快速检查清单(上线前必看)
- [ ] 生产环境的 secrets 是否由环境注入(不在 jar 内)?
- [ ]
spring.profiles.active的设置方式是否明确(命令行 / env / cloud)? - [ ] 是否对关键配置做了校验(
@Validated/ JSR-303)? - [ ] 是否在 Docker / K8s / CI 中正确传递了配置(env、configMap、secrets)?
- [ ] 是否写好了默认值与兜底(
@Value("${prop:default}")或= default)? - [ ] 是否在日志或监控里暴露过多敏感信息(避免把 secret 打到 log)?
- [ ] 是否使用合适的数据类型(
Duration、DataSize等)而不是字符串侥幸?
8. 结语 — 少点魔法,多点显式 🎯
配置管理看似“繁琐”,但搞好后能带来巨大收益:环境一致性、发版可靠性、运维可控性。优先使用 @ConfigurationProperties 做结构化绑定,配合 JSR-303 校验做“早发现”;profile 与外部化配置配合使用能让你在本地/测试与生产间平滑切换;最后,记住优先级规则 —— 当配置不生效时,逐层排查(命令行 > env > 外部文件 > classpath)。
如果你愿意,我可以在当前会话把上面的示例变成一个可直接运行的最小项目(包含 Gradle、完整源代码、Dockerfile、compose),并提供启动命令与测试 curl 请求 — 你想要我把项目代码贴出来,还是直接生成可下载的 ZIP 包?🚀
… …
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
… …
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。
版权声明:本文由作者原创,转载请注明出处,谢谢支持!
- 点赞
- 收藏
- 关注作者
评论(0)