Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring @Configuration file with PropertyPlaceholderConfigurer bean doesn't resolve @Value annotation

Tags:

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?

like image 295
Oleksii Duzhyi Avatar asked Oct 26 '15 11:10

Oleksii Duzhyi


2 Answers

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; } 
like image 84
Alexander Pranko Avatar answered Oct 06 '22 23:10

Alexander Pranko


For any other poor souls who couldn't get this to work in some Configuration classes when they work in others:

Look to see what other beans you have in that class and if any of them get instantiated early in the ApplicationContext. A ConversionService is an example of one. This would instantiate the Configuration class before what is required is registered, thereby making no property injection take place.

I fixed this by moving the ConversionService to another Configuration class that I Imported.

like image 37
George Hilios Avatar answered Oct 06 '22 23:10

George Hilios