23

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?

Oleksii Duzhyi
  • 1,203
  • 3
  • 12
  • 26

3 Answers3

43

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;
}
Alexander Pranko
  • 1,859
  • 17
  • 20
  • 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 – Dhiraj Gandhi Aug 24 '18 at 08:18
  • 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? – Koray Tugay Aug 19 '20 at 01:26
7

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.

George Hilios
  • 199
  • 1
  • 9
  • 1
    Another possibility is to use constructor injection, as described here: https://stackoverflow.com/questions/28636060/spring-value-often-null – jmgonet Apr 01 '19 at 10:16
  • If the first answer doesn't work, this is likely to be the case. It saved me a lot of head scratch. – B.Z. Jul 13 '23 at 16:16
2

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.

Paulius Matulionis
  • 23,085
  • 22
  • 103
  • 143
  • Unfortunately it doesn't work in configuration where I have PropertyPlaceholderConfigurer bean creation, but works in any another configuration file – Oleksii Duzhyi Oct 26 '15 at 12:57