PropertyPlaceholder
and friends do not put the properties in your Environment
(mainly because of backward compatibility reasons). Instead they use Environment and its own internal Properties
object gathered generally from property files from the classpath to resolve @Value
properties. Thus the properties loaded from PropertyPlaceholder can not be fetched dynamically (ie no getProperty(String..)
).
Some people create custom PropertyPlaceholder
that store the properties publicly (through getter or whatever) but I think completely defeats Spring's new unified environment configuration handling.
What you really want is probably @PropertySource
which still is pretty crappy since its not dynamic (since its an annotation you can't change where files get loaded from) but it will load properties into the Environment
. I have been meaning to file issues with Spring Source about the confusion of this.
Anyway you can look at my solution here: Manually add a @PropertySource: Configuring Environment before context is refreshed
Basically you need to get hold of ConfigurableEnvironment
and load your properties into it by creating PropertySources
. The API for this is very powerful but not very intuitive. You can use ApplicationContextInitializers
to get the Environment
which has its own annoying issues (see link) or you can do what I do below.
public class ConfigResourcesEnvironment implements
ResourceLoaderAware, EnvironmentAware, BeanDefinitionRegistryPostProcessor, EnvironmentPropertiesMapSupplier {
private Environment environment;
private Map<String, String> environmentPropertiesMap;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment env = ((ConfigurableEnvironment) this.environment);
List<PropertySource> propertySources;
try {
propertySources = loadPropertySources(); //Your custom method for propertysources
} catch (IOException e) {
throw new RuntimeException(e);
}
//Spring prefers primacy ordering so we reverse the order of the sources... You may not need to do this.
reverse(propertySources);
for (PropertySource rp : propertySources) {
env.getPropertySources().addLast(rp);
}
environmentPropertiesMap = ImmutableMap.copyOf(environmentPropertiesToMap(env));
}
else {
environmentPropertiesMap = ImmutableMap.of();
}
}
public static Map<String,String> environmentPropertiesToMap(ConfigurableEnvironment e) {
Map<String, String> properties = newLinkedHashMap();
for (String n : propertyNames(e.getPropertySources())) {
String v = e.getProperty(n);
if (v != null)
properties.put(n, v);
}
return properties;
}
public static Iterable<String> propertyNames(PropertySources propertySources) {
LinkedHashSet<String> propertyNames = new LinkedHashSet<String>();
for (PropertySource<?> p : propertySources) {
if (p instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> e = (EnumerablePropertySource<?>) p;
propertyNames.addAll(asList(e.getPropertyNames()));
}
}
return propertyNames;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//NOOP
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Map<String, String> getEnvironmentPropertiesMap() {
return environmentPropertiesMap;
}
}
Once you have ConfigurableEnvironment
loaded you can use the EnvironmentAware
interface for things that need the Environment or create your own interface.
Here is a custom interface you can use for things that need dynamic properties (the above class implements it):
public interface EnvironmentPropertiesMapSupplier {
public Map<String, String> getEnvironmentPropertiesMap();
}