我有一个具有以下值的属性文件:
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
?@ConfigurationProperties(prefix="test")
来实现这一点,但是如何使用 Spring MVC 来实现同样的效果。
请参阅 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));
}
}
}
}
}
我遇到了类似的问题并使用以下代码解决了它:
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中所示)是我对属性源进行反向排序,以确保发生适当的属性覆盖。默认情况下,属性源按从最重要到最不重要的顺序排序(另请参阅这篇文章)。因此,如果您在迭代之前不反转列表,您将始终得到最不重要的属性值。
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()));
}
}
给定以下简单对象:
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!
}
api.about
开头的环境中的所有键绑定到我们的目标。
取回值!
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 简化返回值
Properties properties = new Properties();
configuration.getKeys("test.")
.forEachRemainingKey(key -> properties.setProperty(key, configuration.getString(key));
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));
}
}
}
}
}
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;
}