【发布时间】:2020-03-04 03:39:16
【问题描述】:
所以我尝试使用存储在 Cosul 中的键/值来覆盖 application.properties 中的值。我尝试了两件事。
1) 使用 Spring Cloud Consul 配置。 https://cloud.spring.io/spring-cloud-consul/reference/html/#spring-cloud-consul-config
如果我的 application.properties 中没有定义相同的键,这会起作用。如果它是在 application.properties 中定义的,则属性文件中的值将用于所有 @Value 注释解析。这与我想要的相反。
2) 由于上述方法不起作用,我继续创建自定义 EnvironmentPostProcessor。我首先尝试构建一个 MapPropertySource 并使用 environment.getPropertySources().addAfter(..)。这与上面的结果相同。然后我尝试遍历所有属性源,找到名称包含“applicationConfig:[classpath:/application”的属性源,并设置属性值(如果存在)或放置一个新的属性值。此外,我将 MapPropertySource 添加到“applicationConfig: [classpath:/application” 属性源所在的同一 EnumerableCompositePropertySource 中。
无论采用哪种方法,结果都是一样的。如果 key 存在于 application.properties 中,则使用该值。
什么给了?我实际上是在覆盖属性源中的值,并且在 PostProcessor 完成它的工作之前,我可以在调试器中看到这些值。 application.properties 值如何仍然到达@Value 注释?
这是我当前的后处理器。
@Order(Ordered.LOWEST_PRECEDENCE)
public class ConsulPropertyPostProcessor implements EnvironmentPostProcessor {
private static final String PROPERTY_SOURCE_NAME = "applicationConfigurationProperties";
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
PropertySource<?> system = environment.getPropertySources().get(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
ConsulKVService consulKVService = new ConsulKVServiceImpl().instantiateConsulKVServiceImpl((String)system.getProperty("CONSUL_HOST"), (String)system.getProperty("CONSUL_TOKEN"));
Map<String, Object> map = consulKVService.getConsulKeysAndValuesByPrefix((String)system.getProperty("CONSUL_PREFIX"));
addOrReplace(environment.getPropertySources(), map);
}
private void addOrReplace(MutablePropertySources propertySources, Map<String, Object> map) {
MapPropertySource target = new MapPropertySource("applicationConfig: [consulKVs]", map);
if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
PropertySource<?> applicationConfigurationPropertySources = propertySources.get(PROPERTY_SOURCE_NAME);
for(EnumerableCompositePropertySource applicationPropertySource : (ArrayList<EnumerableCompositePropertySource>)applicationConfigurationPropertySources.getSource()){
if(applicationPropertySource.getName() != null
&& applicationPropertySource.getName().contains("applicationConfig: [profile=")) {
for(PropertySource singleApplicationPropertySource : applicationPropertySource.getSource()){
if(singleApplicationPropertySource.getName().contains("applicationConfig: [classpath:/application")){
for (String key : map.keySet()) {
if(map.get(key) != null) {
if (singleApplicationPropertySource.containsProperty(key)) {
((Properties) singleApplicationPropertySource.getSource())
.setProperty(key, (String) map.get(key));
} else {
((Properties) singleApplicationPropertySource.getSource()).put(key, (String) map.get(key));
}
}
}
break;
}
}
applicationPropertySource.add(target);
break;
}
}
}
}
}
提前谢谢大家。
编辑: 尝试覆盖 ApplicationListener 类的 onApplicationEvent 方法,结果与上述相同。这是那个代码。
@Log4j
public class ConsulProperties implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
static ConfigurableEnvironment configurableEnvironment;
private static final String PROPERTY_SOURCE_NAME = "applicationConfigurationProperties";
public static ConfigurableEnvironment getConfigurableEnvironment() {
return configurableEnvironment;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
log.info("Received ApplicationEnvironmentPreparedEvent...");
ConfigurableEnvironment environment = event.getEnvironment();
configurableEnvironment = environment;
Properties props = new Properties();
ConsulKVService consulKVService = new ConsulKVServiceImpl()
.instantiateConsulKVServiceImpl((String) configurableEnvironment.getProperty("CONSUL_HOST"),
(String) configurableEnvironment.getProperty("CONSUL_TOKEN"));
Map<String, Object> map = consulKVService.getConsulKeysAndValuesByPrefix((String) configurableEnvironment.getProperty("CONSUL_PREFIX"));
while(map.values().remove(null));
addOrReplace(environment.getPropertySources(), map);
}
private void addOrReplace(MutablePropertySources propertySources, Map<String, Object> map) {
MapPropertySource target = new MapPropertySource("applicationConfig: [consulKVs]", map);
if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
PropertySource<?> applicationConfigurationPropertySources = propertySources.get(PROPERTY_SOURCE_NAME);
for(EnumerableCompositePropertySource applicationPropertySource : (ArrayList<EnumerableCompositePropertySource>)applicationConfigurationPropertySources.getSource()){
if(applicationPropertySource.getName() != null
&& applicationPropertySource.getName().contains("applicationConfig: [profile=")) {
for(PropertySource singleApplicationPropertySource : applicationPropertySource.getSource()){
if(singleApplicationPropertySource.getName().contains("applicationConfig: [classpath:/application")){
for (String key : map.keySet()) {
if (singleApplicationPropertySource.containsProperty(key)) {
((Properties) singleApplicationPropertySource.getSource())
.setProperty(key, (String) map.get(key));
} else {
((Properties) singleApplicationPropertySource.getSource()).put(key,
map.get(key));
}
}
applicationPropertySource.add(target);
Properties properties = new Properties();
properties.putAll(map);
propertySources.addLast(new PropertiesPropertySource("consulKVs", properties));
break;
}
}
break;
}
}
}
}
}
【问题讨论】:
-
application.properties 中定义的属性会覆盖其他来源的属性。我认为您可以使用事件侦听器来覆盖属性。检查thetechnojournals.com/2019/10/… 的事件监听器实现。
-
@AshokPrajapati 我尝试实现 ApplicationListener
并在 "public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) " 上使用 Override。使用与我的 OP 类似的代码来执行此操作。虽然结果相同。会将此尝试添加到 OP。 -
你确定你的监听器是由 Spring boot 选择并执行的吗?你是怎么注册的?
-
@AshokPrajapati 当然可以。我使用“org.springframework.context.ApplicationListener=com.[package name removed for privacy].ConsulProperties”在 spring.factories 中注册了它,我可以看到我的“Received ApplicationEnvironmentPreparedEvent ...”日志语句并且断点在应用程序上触发开始。
-
我发现了一个代码问题。请查看我的最新答案。
标签: java spring spring-boot properties-file