如何在Java中读取具有相同前缀的多个Spring属性?

问题描述 投票:0回答:8

我有一个具有以下值的属性文件:

test.cat=cat
test.dog=dog
test.cow=cow
birds=eagle

我想用 Spring MVC 读取属性:

import org.springframework.core.env.Environment;
...
Environment.getProperty("test.cat");

这只会返回

"cat"

如何获取以前缀

test.
开头的所有属性,从而排除
birds

在 Spring Boot 中我们可以使用
@ConfigurationProperties(prefix="test")
来实现这一点,但是如何使用 Spring MVC 来实现同样的效果。

java spring spring-mvc spring-boot
8个回答
15
投票

请参阅 Spring 项目中的 Jira 票证,了解为什么您看不到执行您想要的操作的方法。可以使用最内层循环中的 if 语句进行过滤,这样修改上述链接中的代码片段。我假设您有一个

Environment env
变量并正在寻找
String prefix
:

Map<String, String> properties = new HashMap<>();
if (env instanceof ConfigurableEnvironment) {
    for (PropertySource<?> propertySource : ((ConfigurableEnvironment) env).getPropertySources()) {
        if (propertySource instanceof EnumerablePropertySource) {
            for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) {
                if (key.startsWith(prefix)) {
                    properties.put(key, propertySource.getProperty(key));
                }
            }
        }
    }
}

5
投票

我遇到了类似的问题并使用以下代码解决了它:

final var keyPrefix = "test";
final var map = new HashMap<String, Object>();
final var propertySources = ((AbstractEnvironment) environment).getPropertySources().stream().collect(Collectors.toList());
Collections.reverse(propertySources);
for (PropertySource<?> source : propertySources) {
    if (source instanceof MapPropertySource) {
        final var mapProperties = ((MapPropertySource) source).getSource();
        mapProperties.forEach((key, value) -> {
            if (key.startsWith(keyPrefix)) {
                map.put(key, value instanceof OriginTrackedValue ? ((OriginTrackedValue) value).getValue() : value);
            }
        });
    }
}
return map;

与其他解决方案的区别(如Jira Ticket中所示)是我对属性源进行反向排序,以确保发生适当的属性覆盖。默认情况下,属性源按从最重要到最不重要的顺序排序(另请参阅这篇文章)。因此,如果您在迭代之前不反转列表,您将始终得到最不重要的属性值。


3
投票
试试这个方法。在您的示例中,调用

getAllPropWithPrefix("test")

 将返回包含 
{test.cat=cat,test.dog=dog,test.cow=cow}
 的 Map

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.bind.BindResult; import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; import java.util.Collections; import java.util.Map; import java.util.stream.Collectors; @Component public class SpringPropertiesUtil { @Autowired private ConfigurableApplicationContext applicationContext; /** * Get all properties with specific prefix. */ public Map<String,String> getAllPropWithPrefix(String prefix) { BindResult<Map<String, String>> result = Binder.get(applicationContext.getEnvironment()) .bind(prefix, Bindable.mapOf(String.class, String.class)); if (!result.isBound() || result.get()==null) { return Collections.emptyMap(); } return result.get().entrySet().stream().collect(Collectors.toMap(x->prefix+"."+x.getKey(),x->x.getValue())); } }
    

1
投票
使用现有 API 有更雄辩的解决方案:

给定以下简单对象:

internal class AboutApi { lateinit var purpose: String lateinit var version: String lateinit var copyright: String lateinit var notice: String lateinit var contact: String lateinit var description: String }
还有 Spring 环境的实例 (

org.springframework.core.env.Environment

),从环境中提取 
AboutApi
 的值就变得像 1-2-3 一样简单:

private fun about() : AboutApi { val binder = Binder.get(env) // Step - 1 val target = Bindable.ofInstance(AboutApi()) // Step - 2 binder.bind("api.about", target) // Step - 3 return target.value.get() // Finally! }

    获取一个从环境中获取值的活页夹。
  1. 声明您要绑定的目标。
  2. 将以
  3. api.about
     开头的环境中的所有键绑定到我们的目标。
最后——

取回值!


1
投票
在搜索如何在 Spring 应用程序中获取具有给定前缀的所有属性的映射时遇到这个问题,我能够从 @Joseph 获取答案,并提出我认为最简单直接的创建方法这样的地图:

private static Map<String, String> getPropertiesPrefixedWith(ApplicationContext context, String prefix) { return Binder.get(applicationContext.getEnvironment()) .bind(prefix, Bindable.mapOf(String.class, String.class)) .orElse(Collections.emptyMap()); }
BindResult,由 Binder::bind 返回,是一个像Optional一样的单子类型,它在内部检查值的存在/绑定,所以我们可以用 orElse 简化返回值


0
投票
我使用这种方法在 Spring 中加载具有特定前缀的属性(假设您已在组件中注入了 Configuration):

Properties properties = new Properties(); configuration.getKeys("test.") .forEachRemainingKey(key -> properties.setProperty(key, configuration.getString(key));
    

0
投票
Manos 和 Yoran 的回答很有帮助。我发现对 Manos 的代码进行一个小改动就能解决 Yoran 的担忧。基本上,相同的属性可能会出现多次,例如来自环境变量和 application.yml,按重要性降序排列。因此,我们可以通过检查

key

 是否已经不在 
properties
 中来获取第一个实例,如下面改进的代码所示:

Map<String, String> properties = new HashMap<>(); if (env instanceof ConfigurableEnvironment) { for (PropertySource<?> propertySource : ((ConfigurableEnvironment) env).getPropertySources()) { if (propertySource instanceof EnumerablePropertySource) { for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) { if (key.startsWith(prefix) && !properties.containsKey(key)) { properties.put(key, propertySource.getProperty(key)); } } } } }
    

0
投票
假设我们要读取前缀为

spark.

 的所有属性

private static final String SPARK_PREFIX = "spark."; @Bean Properties sparkProperties(final Environment environment) { if (environment instanceof ConfigurableEnvironment) { List<PropertySource<?>> propertySources = ((ConfigurableEnvironment) environment) .getPropertySources().stream().collect(Collectors.toList()); List<String> sparkPropertyNames = propertySources.stream() .filter(propertySource -> propertySource instanceof EnumerablePropertySource) .map(propertySource -> (EnumerablePropertySource) propertySource) .map(EnumerablePropertySource::getPropertyNames) .flatMap(Arrays::stream) .distinct() .filter(key -> key.startsWith(SPARK_PREFIX)) .collect(Collectors.toList()); return sparkPropertyNames.stream() .collect( Properties::new, (props, key) -> props.put(key, environment.getProperty(key)), Properties::putAll); } else { return new Properties(); } }
然后我们可以在任何地方使用它,如下所示。 @Qualizes 很重要,因为 Spring boot 定义了另一个 

Properties

 类型的 bean,用于 
systemProperties

@Bean SparkConf sparkConf(@Qualifier("sparkProperties") final Properties sparkProperties) { final SparkConf sparkConf = new SparkConf(); sparkProperties.forEach((key, value) -> sparkConf.set(key.toString(), value.toString())); return sparkConf; }
    
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.