Issue
I have following configuration file:
@Configuration
public class PropertyPlaceholderConfigurerConfig {
@Value("${property:defaultValue}")
private String property;
@Bean
public static PropertyPlaceholderConfigurer ppc() throws IOException {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocations(new ClassPathResource("properties/" + property + ".properties"));
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
}
I run my application with following VM option:
-Dproperty=propertyValue
So I'd like my application to load specific property file on startup. But for some reason at this stage @Value
annotations are not processed and property is null
. On the other hand if I have PropertyPlaceholderConfigurer
configured via xml file - everything works perfectly as expected. Xml file example:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/>
<property name="location">
<value>classpath:properties/${property:defaultValue}.properties</value>
</property>
</bean>
If I try to to inject property value in another Spring configuration file - it is properly injected. If I move my PropertyPlaceholderConfigurer
bean creation to that configuration file - field value is null again.
As workaround, I use this line of code:
System.getProperties().getProperty("property", "defaultValue")
Which is also works, but I'd like to know why such behavior is occurs and maybe it is possible to rewrite it in other way but without xml?
Solution
From Spring JavaDoc:
In order to resolve ${...} placeholders in definitions or @Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using context:property-placeholder in XML, but must be explicitly registered using a static @Bean method when using @Configuration classes. See the "Working with externalized values" section of @Configuration's javadoc and "a note on BeanFactoryPostProcessor-returning @Bean methods" of @Bean's javadoc for details and examples.
So, you are trying to use a placeholder in the code block required to enable placeholder processing.
As @M.Deinum mentioned, you should use a PropertySource (default or custom implementation).
Example below shows how to use properties in a PropertySource annotation as well as how to inject properties from the PropertySource in a field.
@Configuration
@PropertySource(
value={"classpath:properties/${property:defaultValue}.properties"},
ignoreResourceNotFound = true)
public class ConfigExample {
@Value("${propertyNameFromFile:defaultValue}")
String propertyToBeInjected;
/**
* Property placeholder configurer needed to process @Value annotations
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Update 09/2021
as Koray mentioned in the comment, the PropertySourcesPlaceholderConfigurer
is not needed anymore since Spring 4.3+ / Spring Boot 1.5+. Dynamic filenames can be used for property files in @PropertySource
and @ConfigurationProperties
annotations without additional configuration.
@Configuration
@PropertySource(
value={"classpath:properties/${property:defaultValue}.properties"},
ignoreResourceNotFound = true)
public class ConfigExample {
@Value("${propertyNameFromFile:defaultValue}")
String propertyToBeInjected;
}
@ConfigurationProperties("properties/${property:defaultValue}.properties")
public class ConfigExample {
String propertyNameFromFile;
}
Answered By - Alexander Pranko