Programmatic access to properties created by property-placeholder

@Value

annotation works on new releases of Spring (tested on v3.2.2) Here is how it is done:

  1. Map your properties file in spring configuration file

    <!--Import Info:
    xmlns:context="http://www.springframework.org/schema/context"
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.2.xsd-->
    
    <context:property-placeholder location="classpath:/app-config.properties" />
    
  2. Create app-config.properties inside (root) your source folder

    my.property=test
    my.property2=test2
    
  3. Create a controller class

    @Controller
    public class XRDSBuilder
    {
        @Value("${my.property}")
        private String myProperty;
    
        public String getMyProperty() { return myProperty; }
    }
    

Spring will automatically map the content of my.property to your variable inside the controller

Mapping to a list

Property value:

my.list.property=test,test2,test3

Controller class configuration:

@Value("#{'${my.list.property}'.split(',')}")
private List<String> myListProperty;

Advanced mapping

@Component("PropertySplitter")
public class PropertySplitter {

    /**
     * Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2
     */
    public Map<String, String> map(String property) {
        return this.map(property, ",");
    }

    /**
     * Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2
     */
    public Map<String, List<String>> mapOfList(String property) {
        Map<String, String> map = this.map(property, ";");

        Map<String, List<String>> mapOfList = new HashMap<>();
        for (Entry<String, String> entry : map.entrySet()) {
            mapOfList.put(entry.getKey(), this.list(entry.getValue()));
        }

        return mapOfList;
    }

    /**
     * Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4
     */
    public List<String> list(String property) {
        return this.list(property, ",");
    }

    /**
     * Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2
     */
    public List<List<String>> groupedList(String property) {
        List<String> unGroupedList = this.list(property, ";");

        List<List<String>> groupedList = new ArrayList<>();
        for (String group : unGroupedList) {
            groupedList.add(this.list(group));
        }

        return groupedList;

    }

    private List<String> list(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property);
    }

    private Map<String, String> map(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property);
    }
}

Property value:

my.complex.property=test1:value1,test2:value2

Controller class:

@Value("#{PropertySplitter.map('${my.complex.property}')}")
Map<String, String> myComplexProperty;

No you can't. PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor, it is only "alive" during bean creation. When it encounters a ${property} notation, it tries to resolve that against its internal properties, but it does not make these properties available to the container.

That said: similar questions have appeared again and again, the proposed solution is usually to subclass PropertyPlaceHolderConfigurer and make the Properties available to the context manually. Or use a PropertiesFactoryBean


We use the following approach to access properties for our applications

<util:properties id="appProperties" location="classpath:app-config.properties" />
<context:property-placeholder properties-ref="appProperties"/>

Then you have the luxury of just autowiring properties into beans using a qualifier.

@Component
public class PropertyAccessBean {

    private Properties properties;

    @Autowired
    @Qualifier("appProperties")
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void doSomething() {
        String property = properties.getProperty("code.version");
    }

}

If you have more complex properties you can still use ignore-resource-not-found and ignore-unresolvable. We use this approach to externalise some of our application settings.

 <util:properties id="appProperties" ignore-resource-not-found="true"
    location="classpath:build.properties,classpath:application.properties,
                            file:/data/override.properties"/>
 <context:property-placeholder ignore-unresolvable="true" properties-ref="appProperties"/>