1. 概述
在本文中,我们将学习如何在 Spring 应用中重新加载属性文件。Spring 提供了多种方式来读取属性,但在运行时动态更新属性并不是默认支持的功能。我们会介绍几种实现方式,包括手动实现和使用 Spring Cloud 的自动刷新机制。
2. 在 Spring 中读取属性
Spring 提供了多种方式来访问配置属性:
✅ Environment:通过注入 Environment
实例,使用 getProperty()
方法读取属性值。Environment
包含多个属性源(如系统属性、命令行参数、application.properties
等),也可以通过 @PropertySource
添加自定义属性源。
✅ Properties:将属性文件加载为 Properties
实例,然后通过 properties.get("key")
获取属性值。
✅ @Value:使用 @Value("${key}")
注解直接将属性值注入到 Bean 中。
✅ @ConfigurationProperties:适用于结构化配置,通过绑定属性前缀到一个 Java Bean 来使用。
3. 从外部文件重新加载属性
若想在运行时更新属性,最简单的方式是将配置文件放在 jar 包之外,通过启动参数 --spring.config.location=file://path-to-file
告知 Spring 配置文件的位置。
但 Spring 默认不会自动监听文件变化,我们需要手动实现或借助工具来实现自动刷新。
一个常用的工具是 Apache Commons Configuration,它提供了自动检测文件修改并重新加载的功能。
添加依赖
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
创建 PropertiesConfiguration
Bean
@Bean
@ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false)
public PropertiesConfiguration propertiesConfiguration(
@Value("${spring.config.location}") String path) throws Exception {
String filePath = new File(path.substring("file:".length())).getCanonicalPath();
PropertiesConfiguration configuration = new PropertiesConfiguration(new File(filePath));
configuration.setReloadingStrategy(new FileChangedReloadingStrategy());
return configuration;
}
这里我们使用了 FileChangedReloadingStrategy
,它默认每 5 秒检查一次文件是否被修改。可以通过 setRefreshDelay(long)
设置检查间隔。
3.1. 重新加载 Environment 属性
如果我们希望通过 Environment
获取动态更新的属性值,需要自定义一个 PropertySource
,并使用 PropertiesConfiguration
提供最新的值。
实现 ReloadablePropertySource
public class ReloadablePropertySource extends PropertySource {
private final PropertiesConfiguration propertiesConfiguration;
public ReloadablePropertySource(String name, PropertiesConfiguration propertiesConfiguration) {
super(name);
this.propertiesConfiguration = propertiesConfiguration;
}
@Override
public Object getProperty(String key) {
return propertiesConfiguration.getProperty(key);
}
}
将其加入 Environment
@Configuration
public class ReloadablePropertySourceConfig {
private final ConfigurableEnvironment env;
public ReloadablePropertySourceConfig(@Autowired ConfigurableEnvironment env) {
this.env = env;
}
@Bean
@ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false)
public ReloadablePropertySource reloadablePropertySource(PropertiesConfiguration properties) {
ReloadablePropertySource ret = new ReloadablePropertySource("dynamic", properties);
MutablePropertySources sources = env.getPropertySources();
sources.addFirst(ret); // 保证优先级最高
return ret;
}
}
使用示例
@Component
public class EnvironmentConfigBean {
private final Environment environment;
public EnvironmentConfigBean(@Autowired Environment environment) {
this.environment = environment;
}
public String getColor() {
return environment.getProperty("application.theme.color");
}
}
⚠️ 注意:我们把自定义的 PropertySource
放在首位,确保它能覆盖其他同名属性。
3.2. 重新加载 Properties 实例
如果你使用的是 Properties
实例,可以继承 Properties
并重写 getProperty
方法:
public class ReloadableProperties extends Properties {
private final PropertiesConfiguration propertiesConfiguration;
public ReloadableProperties(PropertiesConfiguration propertiesConfiguration) throws IOException {
super.load(new FileReader(propertiesConfiguration.getFile()));
this.propertiesConfiguration = propertiesConfiguration;
}
@Override
public String getProperty(String key) {
String val = propertiesConfiguration.getString(key);
super.setProperty(key, val);
return val;
}
}
然后将其注册为 Bean 即可。
3.3. 使用 @ConfigurationProperties 刷新 Bean
@ConfigurationProperties
默认绑定的是静态 Bean。若想实现动态刷新,需要配合 Spring Cloud 的 @RefreshScope
使用。
⚠️ 注意:Spring 默认的 Bean 作用域是 singleton
,@RefreshScope
会将其改为 refresh
,在属性更新后重新创建 Bean。
3.4. 使用 @Value 刷新 Bean
与 @ConfigurationProperties
类似,@Value
也受作用域限制。若想实现刷新,也需要配合 @RefreshScope
使用。
4. 使用 Actuator 和 Spring Cloud 实现刷新
Spring Boot Actuator 提供了 /actuator/refresh
接口,可以触发属性刷新。但这个功能依赖于 Spring Cloud。
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
并在 pom.xml
中引入 Spring Cloud 版本管理:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
启用 refresh 接口
management.endpoints.web.exposure.include=refresh
调用 /actuator/refresh
后,Spring 会重新加载所有属性源,并触发 EnvironmentChangeEvent
。
4.1. 使用 @ConfigurationProperties + @RefreshScope
@Component
@ConfigurationProperties(prefix = "application.theme")
@RefreshScope
public class ConfigurationPropertiesRefreshConfigBean {
private String color;
public void setColor(String color) {
this.color = color;
}
// getter
}
⚠️ 注意:@ConfigurationProperties
要求必须有 setter 方法。
4.2. 使用 @Value + @RefreshScope
@Component
@RefreshScope
public class ValueRefreshConfigBean {
private String color;
public ValueRefreshConfigBean(@Value("${application.theme.color}") String color) {
this.color = color;
}
// getter
}
⚠️ 注意:如果 Bean 被显式标记为 @Scope("singleton")
,/actuator/refresh
将不会生效。
5. 总结
本文介绍了多种在 Spring 中实现属性动态刷新的方式:
✅ 手动实现:通过 PropertiesConfiguration
+ 自定义 PropertySource
实现属性文件监听和更新。
✅ 使用 Spring Cloud:通过 @RefreshScope
+ /actuator/refresh
实现自动刷新,适用于 @Value
和 @ConfigurationProperties
。
⚠️ 注意事项:
- 不同作用域的 Bean 行为不同,
singleton
不会自动刷新。 @ConfigurationProperties
必须提供 setter 方法。@Value
构造注入的方式在刷新时不会重新执行构造函数,需谨慎使用。
通过上述方法,你可以根据项目需求选择合适的方式来实现属性的动态更新。