我正在使用注释来配置我的弹簧环境:
@Configuration
...
@PropertySource("classpath:/config/default.properties")
...
public class GeneralApplicationConfiguration implements WebApplicationInitializer
{
@Autowired
Environment env;
}
这会导致我default.properties
的属性成为Environment
的一部分。我想在这里使用@PropertySource
机制,因为它已经提供了基于环境设置(例如config_dir位置)通过几个回退层和不同动态位置来重载属性的可能性。我只是删除了后备以使示例更容易。
但是,我现在的问题是我想在default.properties
中配置我的数据源属性。您可以将设置传递给数据源,而无需详细了解数据源所需的设置
Properties p = ...
datasource.setProperties(p);
然而,问题是,Environment
对象既不是Properties
对象也不是Map
,也不是可比较的对象。从我的角度来看,根本无法访问环境的所有值,因为没有keySet
或iterator
方法或任何可比较的方法。
Properties p <=== Environment env?
我错过了什么吗?是否有可能以某种方式访问Environment
对象的所有条目?如果是,我可以将条目映射到Map
或Properties
对象,我甚至可以通过前缀过滤或映射它们 - 创建子集作为标准java Map
...这就是我想要做。有什么建议吗?
答案 0 :(得分:58)
你需要这样的东西,也许它可以改进。这是第一次尝试:
...
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
...
@Configuration
...
@org.springframework.context.annotation.PropertySource("classpath:/config/default.properties")
...
public class GeneralApplicationConfiguration implements WebApplicationInitializer
{
@Autowired
Environment env;
public void someMethod() {
...
Map<String, Object> map = new HashMap();
for(Iterator it = ((AbstractEnvironment) env).getPropertySources().iterator(); it.hasNext(); ) {
PropertySource propertySource = (PropertySource) it.next();
if (propertySource instanceof MapPropertySource) {
map.putAll(((MapPropertySource) propertySource).getSource());
}
}
...
}
...
基本上,来自环境的MapPropertySource
(并且有很多实现)的所有内容都可以作为Map
属性进行访问。
答案 1 :(得分:44)
这是一个老问题,但接受的答案有严重的缺陷。如果Spring Environment
对象包含任何重写值(如Externalized Configuration中所述),则无法保证它生成的属性值映射将与Environment
对象返回的属性值匹配。我发现,简单地遍历PropertySource
Environment
的{{1}}实际上没有给出任何重要的值。相反,它产生了原始值,应该被覆盖的值。
这是一个更好的解决方案。这使用EnumerablePropertySource
的{{1}}来迭代已知的属性名称,但随后从真实的Spring环境中读取实际值。这保证了值是Spring实际解决的值,包括任何重写值。
Environment
答案 2 :(得分:17)
我需要检索其键以不同前缀开头的所有属性(例如,以“log4j.appender。”开头的所有属性)并编写以下代码(使用Java 8的流和lamdas)。
public static Map<String,Object> getPropertiesStartingWith( ConfigurableEnvironment aEnv,
String aKeyPrefix )
{
Map<String,Object> result = new HashMap<>();
Map<String,Object> map = getAllProperties( aEnv );
for (Entry<String, Object> entry : map.entrySet())
{
String key = entry.getKey();
if ( key.startsWith( aKeyPrefix ) )
{
result.put( key, entry.getValue() );
}
}
return result;
}
public static Map<String,Object> getAllProperties( ConfigurableEnvironment aEnv )
{
Map<String,Object> result = new HashMap<>();
aEnv.getPropertySources().forEach( ps -> addAll( result, getAllProperties( ps ) ) );
return result;
}
public static Map<String,Object> getAllProperties( PropertySource<?> aPropSource )
{
Map<String,Object> result = new HashMap<>();
if ( aPropSource instanceof CompositePropertySource)
{
CompositePropertySource cps = (CompositePropertySource) aPropSource;
cps.getPropertySources().forEach( ps -> addAll( result, getAllProperties( ps ) ) );
return result;
}
if ( aPropSource instanceof EnumerablePropertySource<?> )
{
EnumerablePropertySource<?> ps = (EnumerablePropertySource<?>) aPropSource;
Arrays.asList( ps.getPropertyNames() ).forEach( key -> result.put( key, ps.getProperty( key ) ) );
return result;
}
// note: Most descendants of PropertySource are EnumerablePropertySource. There are some
// few others like JndiPropertySource or StubPropertySource
myLog.debug( "Given PropertySource is instanceof " + aPropSource.getClass().getName()
+ " and cannot be iterated" );
return result;
}
private static void addAll( Map<String, Object> aBase, Map<String, Object> aToBeAdded )
{
for (Entry<String, Object> entry : aToBeAdded.entrySet())
{
if ( aBase.containsKey( entry.getKey() ) )
{
continue;
}
aBase.put( entry.getKey(), entry.getValue() );
}
}
请注意,起点是ConfigurableEnvironment,它能够返回嵌入的PropertySources(ConfigurableEnvironment是Environment的直接后代)。您可以通过以下方式自动装配:
@Autowired
private ConfigurableEnvironment myEnv;
如果您不使用非常特殊类型的属性源(如JndiPropertySource,通常不在spring自动配置中使用),则可以检索环境中保存的所有属性。
实现依赖于spring本身提供的迭代顺序并获取第一个找到的属性,所有后来找到的具有相同名称的属性都将被丢弃。这应该确保与直接向环境询问属性(返回第一个找到的属性)相同的行为。
另请注意,如果返回的属性包含带有$ {...}运算符的别名,则尚未解析它们。如果您想要解决特定密钥,则必须再次直接询问环境:
myEnv.getProperty( key );
答案 3 :(得分:5)
作为Spring's Jira ticket,这是一个有意的设计。但以下代码适用于我。
<a href="https://www.facebook.com/{{actorID.facebook_id}}" target="_blank"><i class="fa fa-facebook-official"></i></a>
答案 4 :(得分:3)
最初的问题暗示,能够基于前缀过滤所有属性会很好。我刚刚确认,对于Properties
或 Map<String,String>
,此版本从Spring Boot 2.1.1.RELEASE开始有效。我敢肯定它已经工作了一段时间了。有趣的是,如果没有prefix =
资格,它就无法工作,也就是说,我不不知道如何将整个环境加载到地图中。如我所说,这实际上可能是OP想要开始的。前缀和后面的“。”将被剥夺,这可能不是人们想要的东西:
@ConfigurationProperties(prefix = "abc")
@Bean
public Properties getAsProperties() {
return new Properties();
}
@Bean
public MyService createService() {
Properties properties = getAsProperties();
return new MyService(properties);
}
后记:确实有可能并且很容易获得整个环境。我不知道这是怎么逃脱的:
@ConfigurationProperties
@Bean
public Properties getProperties() {
return new Properties();
}
答案 5 :(得分:1)
其他答案指出了涉及PropertySources
的大多数案件的解决方案,但没有人提到某些财产来源无法投入有用的类型。
一个这样的例子是命令行参数的属性源。使用的类是SimpleCommandLinePropertySource
。此私有类由 public 方法返回,因此访问对象内的数据非常棘手。我不得不使用反射来读取数据并最终替换属性源。
如果有人有更好的解决方案,我真的很想看到它;然而,这是我唯一能够工作的黑客。
答案 6 :(得分:1)
Spring不允许通过java.util.Properties
与Spring Environment解耦。
但是Properties.load()
仍然可以在Spring启动应用程序中工作:
Properties p = new Properties();
try (InputStream is = getClass().getResourceAsStream("/my.properties")) {
p.load(is);
}
答案 7 :(得分:1)
要获取仅在我的 hibernate.properteies
文件中定义的属性:
@PropertySource(SomeClass.HIBERNATE_PROPERTIES)
public class SomeClass {
public static final String HIBERNATE_PROPERTIES = "hibernate.properties";
@Autowired
private Environment env;
public void someMethod() {
final Properties hibProps = asProperties(HIBERNATE_PROPERTIES);
}
private Properties asProperties(String fileName) {
return StreamSupport.stream(
((AbstractEnvironment) env).getPropertySources().spliterator(), false)
.filter(ps -> ps instanceof ResourcePropertySource)
.map(ps -> (ResourcePropertySource) ps)
.filter(rps -> rps.getName().contains(fileName))
.collect(
Properties::new,
(props, rps) -> props.putAll(rps.getSource()),
Properties::putAll);
}
}
答案 8 :(得分:0)
请尝试以下代码:
...
@Autowired
private Environment env;
...
for(Iterator<PropertySource<?>> it = ((AbstractEnvironment) env).getPropertySources().iterator(); it.hasNext(); ) {
PropertySource<?> propertySource = (PropertySource<?>) it.next();
if (propertySource instanceof CompositePropertySource) {
for(Iterator<PropertySource<?>> it2 = ((CompositePropertySource) propertySource).getPropertySources().iterator(); it2.hasNext(); ) {
PropertySource<?> propertySource2 = (PropertySource<?>) it2.next();
if (propertySource2 instanceof ResourcePropertySource) {
for (Entry<String, Object> entry : ((ResourcePropertySource)propertySource2).getSource().entrySet()) {
if (entry.getValue() instanceof String) {
System.out.println(entry.getKey() + "=" + (String)entry.getValue());
}
}
}
}
}
}
答案 9 :(得分:0)
使用Spring Boot 2,我需要做类似的事情。上面的大多数答案都可以正常工作,只是要注意,在应用程序生命周期的各个阶段,结果会有所不同。
例如,在ApplicationEnvironmentPreparedEvent
之后,application.properties
内部的任何属性都不存在。但是,发生ApplicationPreparedEvent
事件之后。
答案 10 :(得分:0)
对于Spring Boot,接受的答案将覆盖具有较低优先级的重复属性。此解决方案会将属性收集到SortedMap
中,并且仅采用优先级最高的重复属性。
final SortedMap<String, String> sortedMap = new TreeMap<>();
for (final PropertySource<?> propertySource : env.getPropertySources()) {
if (!(propertySource instanceof EnumerablePropertySource))
continue;
for (final String name : ((EnumerablePropertySource<?>) propertySource).getPropertyNames())
sortedMap.computeIfAbsent(name, propertySource::getProperty);
}
答案 11 :(得分:0)
尽管我会再添加一种方法。就我而言,我将其提供给com.hazelcast.config.XmlConfigBuilder
,后者仅需java.util.Properties
即可解析Hazelcast XML配置文件中的某些属性,即,它仅调用getProperty(String)
方法。因此,这使我能够做自己需要做的事情:
@RequiredArgsConstructor
public class SpringReadOnlyProperties extends Properties {
private final org.springframework.core.env.Environment delegate;
@Override
public String getProperty(String key) {
return delegate.getProperty(key);
}
@Override
public String getProperty(String key, String defaultValue) {
return delegate.getProperty(key, defaultValue);
}
@Override
public synchronized String toString() {
return getClass().getName() + "{" + delegate + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
SpringReadOnlyProperties that = (SpringReadOnlyProperties) o;
return delegate.equals(that.delegate);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), delegate);
}
private void throwException() {
throw new RuntimeException("This method is not supported");
}
//all methods below throw the exception
* override all methods *
}
P.S。我最终没有将其专门用于Hazelcast,因为它只能解析XML文件的属性,而不能在运行时解析。由于我也使用Spring,因此决定使用自定义org.springframework.cache.interceptor.AbstractCacheResolver#getCacheNames
。至少在缓存名称中使用属性的情况下,这可以解析这两种情况的属性。