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

54,830

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.

Share:
54,830

Related videos on Youtube

Oleksii Duzhyi
Author by

Oleksii Duzhyi

Java software engineer

Updated on September 17, 2021

Comments

  • Oleksii Duzhyi
    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 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?

    • M. Deinum
      M. Deinum over 8 years
      First I strongly suggest the use of ProperySourcesPlaceholderConfigurer and use a @PropertySource on your class. Second the bean needs to be static.
    • Oleksii Duzhyi
      Oleksii Duzhyi over 8 years
      @M.Deinum @PropertySource works perfectly for me, but what if I have custom implementation of ProperySourcesPlaceholderConfigurer?
    • M. Deinum
      M. Deinum over 8 years
      Why would you need a custom implementation.
    • Oleksii Duzhyi
      Oleksii Duzhyi over 8 years
      @M.Deinum to load properties from ZooKeeper for example
    • M. Deinum
      M. Deinum over 8 years
      You don't need a custom implementation for that you need a custom PropertySource
  • Oleksii Duzhyi
    Oleksii Duzhyi over 8 years
    Unfortunately it doesn't work in configuration where I have PropertyPlaceholderConfigurer bean creation, but works in any another configuration file
  • Dhiraj Gandhi
    Dhiraj Gandhi over 5 years
    What 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
    jmgonet about 5 years
    Another possibility is to use constructor injection, as described here: stackoverflow.com/questions/28636060/spring-value-often-null
  • Koray Tugay
    Koray Tugay over 3 years
    I 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?