Spring:以Map或Properties对象的forms访问所有Environment属性

我正在使用注释来configuration我的弹簧环境,如下所示:

@Configuration ... @PropertySource("classpath:/config/default.properties") ... public class GeneralApplicationConfiguration implements WebApplicationInitializer { @Autowired Environment env; } 

这导致我的属性从default.properties成为环境的一部分。 我想在这里使用@PropertySource机制,因为它已经提供了基于环境设置(例如config_dir位置)通过多个回退层和不同dynamic位置来重载属性的可能性。 我刚刚剥离了后备,以使示例更容易。

然而,我现在的问题是,我想configuration例如我default.properties中的数据源属性。 您可以将设置传递给数据源,而无需详细了解数据源预期使用的设置

 Properties p = ... datasource.setProperties(p); 

但问题是,Environment对象既不是一个Properties对象,也不是一个Map,也没有任何可比的东西。 从我的angular度来看,访问环境的所有值是不可能的,因为没有keySet或迭代器的方法或任何可比较的东西。

 Properties p <=== Environment env? 

我错过了什么吗? 是否有可能以某种方式访问​​Environment对象的所有条目? 如果是的话,我可以映射到一个地图或属性对象,我甚至可以通过前缀过滤或映射它们 – 创build子集作为一个标准的Java Map …这是我想要做的。 有什么build议么?

你需要这样的东西,也许这是可以改善的,这是第一次尝试:

 ... 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来访问。

这是一个古老的问题,但接受的答案有一个严重的缺陷。 如果Spring Environment对象包含任何重写值(如Externalized Configuration中所述 ),则不能保证它生成的属性值映射将匹配从Environment对象返回的值。 实际上,我发现仅仅遍历EnvironmentPropertySource就没有提供任何压倒一切的值。 相反,它产生了原本应该被忽略的价值。

这是一个更好的解决scheme。 这使用EnvironmentEnumerablePropertySource遍历已知的属性名称,但是然后从实际的Spring环境中读取实际值。 这保证了这个值是Spring实际解决的值,包括任何重载的值。

 Properties props = new Properties(); MutablePropertySources propSrcs = ((AbstractEnvironment) springEnv).getPropertySources(); StreamSupport.stream(propSrcs.spliterator(), false) .filter(ps -> ps instanceof EnumerablePropertySource) .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()) .flatMap(Arrays::<String>stream) .forEach(propName -> props.setProperty(propName, springEnv.getProperty(propName))); 

我有要求检索所有属性的密钥以不同的前缀开始(例如,所有以“log4j.appender。”开头的属性),并写下下面的代码(使用Java 8的stream和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() ); } } 

请注意,起点是能够返回embedded的PropertySources的ConfigurableEnvironment(ConfigurableEnvironment是Environment的直接后裔)。 您可以通过以下方式自动assembly它:

 @Autowired private ConfigurableEnvironment myEnv; 

如果你没有使用非常特殊的属性来源(比如JndiPropertySource,通常在Spring自动configuration中不使用),你可以检索环境中的所有属性。

这个实现依赖于spring本身提供的迭代顺序,并取第一个find的属性,所有后来发现的具有相同名称的属性都被丢弃。 这应该确保相同的行为,如果环境直接要求财产(返回第一个find的)。

另请注意,如果返回的属性包含带有$ {…}运算符的别名,则它们尚未parsing。 如果您想要解决某个特定的密钥,则必须再次直接询问环境:

 myEnv.getProperty( key ); 

作为这个spring的Jira票 ,这是一个故意的devise。 但是下面的代码适用于我。

 public static Map<String, Object> getAllKnownProperties(Environment env) { Map<String, Object> rtn = new HashMap<>(); if (env instanceof ConfigurableEnvironment) { for (PropertySource<?> propertySource : ((ConfigurableEnvironment) env).getPropertySources()) { if (propertySource instanceof EnumerablePropertySource) { for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) { rtn.put(key, propertySource.getProperty(key)); } } } } return rtn; } 

试试下面的代码:

 ... @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()); } } } } } }