Spring Boot 指定数据来源详解
在 Spring Boot 中,配置数据可以从多个来源读取:配置文件、环境变量、命令行参数、系统属性等。框架将这些来源抽象为 PropertySource,并统一聚合到 Environment 对象中。理解"数据来源"的本质,是理解 Spring Boot 配置管理体系的入口。
一、核心概念:PropertySource 与 Environment
PropertySource 是 Spring 中表示一个配置来源的抽象类。每个来源(如 application.yml、系统环境变量、命令行参数)都对应一个 PropertySource 实例,内部存储着一组键值对。
Environment 是所有这些 PropertySource 的聚合视图。当你调用 environment.getProperty("server.port") 时,Spring 会按顺序遍历所有已注册的 PropertySource,返回第一个匹配的值。
java
@Autowired
private Environment environment;
public void printPort() {
String port = environment.getProperty("server.port");
System.out.println("当前端口: " + port);
}
Spring Boot 启动时,会按固定顺序加载多个 PropertySource,后加载的优先级高于先加载的。
二、数据来源的优先级顺序
从高到低排列:
- 命令行参数 (
--server.port=8081) - 操作系统环境变量 (
SERVER_PORT=8081) - JVM 系统属性 (
-Dserver.port=8081) - 外部配置文件 (
config/application-{profile}.yml) - 内部配置文件 (classpath 下的
application-{profile}.yml) - 外部配置文件 (
config/application.yml) - 内部配置文件 (classpath 下的
application.yml) - 外部 properties 文件 (
config/application-{profile}.properties) - 内部 properties 文件 (classpath 下的
application-{profile}.properties) - 外部 properties 文件 (
config/application.properties) - 内部 properties 文件 (classpath 下的
application.properties) @PropertySource加载的自定义配置文件- 默认值
三、各类配置来源详解
1. 命令行参数
bash
java -jar myapp.jar --server.port=8081 --app.name=prod-app
Spring Boot 会将 -- 开头的参数解析为配置键值对。这是最高优先级的来源,适合临时覆盖配置。
2. 操作系统环境变量
bash
export SERVER_PORT=8081
export SPRING_PROFILES_ACTIVE=prod
java -jar myapp.jar
环境变量名中的 . 会被替换为 _,并转为大写。server.port → SERVER_PORT。这是容器化部署(Docker、K8s)中最常用的配置注入方式。
3. JVM 系统属性
bash
java -Dserver.port=8081 -Dapp.name=prod-app -jar myapp.jar
通过 -D 参数传入的属性会直接注入到 System.getProperties() 中。
4. 配置文件(核心来源)
Spring Boot 默认加载 classpath 根目录下的 application.yml 或 application.properties。支持按 Profile 加载 application-{profile}.yml。
配置文件也可放在外部目录,优先级更高:
- 当前目录的
/config子目录 - 当前目录
- classpath 的
/config包 - classpath 根目录
bash
# 指定外部配置文件位置
java -jar myapp.jar --spring.config.location=/opt/config/application.yml
# 额外添加配置文件位置(不覆盖默认)
java -jar myapp.jar --spring.config.additional-location=/opt/extra/
5. @PropertySource 注解
在 @Configuration 类上使用 @PropertySource,加载自定义配置文件:
java
@Configuration
@PropertySource("classpath:custom.properties")
@PropertySource("file:/opt/config/override.properties")
public class AppConfig {
@Value("${custom.value}")
private String value;
}
@PropertySource 加载的配置在所有默认配置文件之后加载,但优先级低于 application.yml。可通过 @PropertySources 加载多个文件,后声明的优先级更高。
6. 默认值
当所有配置来源都找不到某个键时,@Value 的默认值或代码中的默认值生效。
java
@Value("${app.timeout:30}")
private int timeout;
四、随机值来源
Spring Boot 提供了 RandomValuePropertySource,可用于生成随机值:
yaml
app:
secret: ${random.value}
uuid: ${random.uuid}
number: ${random.int(100)}
range: ${random.int(10,20)}
五、查看所有配置来源
1. 打印所有 PropertySource
java
@Autowired
private Environment environment;
public void listPropertySources() {
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
for (PropertySource<?> source : env.getPropertySources()) {
System.out.println(source.getName() + ":" + source.getClass().getSimpleName());
}
}
}
2. 使用 Actuator 端点
引入 spring-boot-starter-actuator,访问 /actuator/env 端点可以查看所有配置属性及其来源。
六、在代码中访问配置
1. 直接注入 Environment
java
@Component
public class MyComponent {
@Autowired
private Environment environment;
public String getConfig(String key) {
return environment.getProperty(key);
}
}
2. 使用 @Value
java
@Value("${app.name}")
private String appName;
@Value("${app.timeout:30}")
private int timeout;
3. 使用 @ConfigurationProperties
java
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private int timeout;
}
4. 获取数组或集合
java
@Value("${app.servers}")
private List<String> servers;
七、动态刷新配置
Spring Cloud 提供了 @RefreshScope 注解,允许在不重启应用的情况下刷新配置:
java
@Component
@RefreshScope
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
}
配合 spring-cloud-starter-config 或 Actuator 的 /actuator/refresh 端点使用。
八、配置优先级验证
在启动类中添加如下代码,验证最终生效的配置值:
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
ConfigurableEnvironment environment = context.getEnvironment();
System.out.println("server.port = " + environment.getProperty("server.port"));
}
}
九、常见问题与排查
配置不生效
检查是否有更高优先级的配置覆盖了你的配置。--debug 启动参数可以查看配置加载详情。
环境变量命名错误
环境变量中的 . 需要替换为 _,且全部大写。app.timeout → APP_TIMEOUT。
Profile 未激活
application-{profile}.yml 只有在对应的 Profile 激活时才加载。检查启动参数 --spring.profiles.active=xxx。
配置文件位置不对
默认加载 classpath 根目录的 application.yml。外部配置放在 config/ 目录下优先级更高。
@PropertySource 加载的文件被覆盖
@PropertySource 加载的配置在默认配置文件之后,但 application.yml 中的同名键会覆盖它。
十、总结
Spring Boot 的配置来源优先级可以概括为:命令行 > 环境变量 > 系统属性 > 外部配置 > 内部配置 > 默认值。
理解这个优先级顺序是排查配置问题的第一步。Environment 是访问配置的统一入口,@Value 和 @ConfigurationProperties 是两种主要的注入方式。在容器化部署中,环境变量是最常用的配置注入方式;在本地开发中,application.yml 是最直接的配置方式;在需要临时覆盖时,命令行参数是最方便的方式。
掌握这些配置来源和优先级,就能在任何环境中灵活管理 Spring Boot 应用的配置。