Spring @Configuration file with PropertyPlaceholderConfigurer bean doesn't resolve @Value annotation
Solution 1
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;
}
Solution 2
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.
Solution 3
If you run your application using VM option and then want to access that option in your application you have to do it slightly different:
@Value("#{systemProperties.property}")
private String property;
Your PropertyPlaceholderConfigurer is not aware of system properties, also note that you are accessing properties using $
- which refers to place holders and #
refers to beans, where systemProperties
is a bean.
Related videos on Youtube
Comments
-
Oleksii Duzhyi over 2 years
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 isnull
. On the other hand if I havePropertyPlaceholderConfigurer
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?
-
M. Deinum over 8 yearsFirst I strongly suggest the use of
ProperySourcesPlaceholderConfigurer
and use a@PropertySource
on your class. Second the bean needs to bestatic
. -
Oleksii Duzhyi over 8 years@M.Deinum
@PropertySource
works perfectly for me, but what if I have custom implementation ofProperySourcesPlaceholderConfigurer
? -
M. Deinum over 8 yearsWhy would you need a custom implementation.
-
Oleksii Duzhyi over 8 years@M.Deinum to load properties from ZooKeeper for example
-
M. Deinum over 8 yearsYou don't need a custom implementation for that you need a custom
PropertySource
-
-
Oleksii Duzhyi over 8 yearsUnfortunately it doesn't work in configuration where I have PropertyPlaceholderConfigurer bean creation, but works in any another configuration file
-
Dhiraj Gandhi over 5 yearsWhat if i want to add additional properties in the bean creation method? I am loading my yaml file properties manually and want that to add in the PropertySources
-
jmgonet about 5 yearsAnother possibility is to use constructor injection, as described here: stackoverflow.com/questions/28636060/spring-value-often-null
-
Koray Tugay over 3 yearsI think Property placeholder configurer needed to process @Value annotations is not true anymore since Spring 4.3 and
propertyConfigurer()
can be removed from the example?