41

I'm reading properties file using context:property-placeholder. How can I access them programatically (@Value doesn't work - I don't know property titles at the moment of developing)?

The main problem is I can't change applicationContext.xml file because it's setted up by "parent" framework

ps. It's strange but Environment.getProperty returns null

fedor.belov
  • 22,343
  • 26
  • 89
  • 134
  • See http://stackoverflow.com/questions/5172392/how-to-programmatically-resolve-property-placeholder-in-spring. It seems to be possible since spring 3. – Vadzim Mar 28 '13 at 15:40

9 Answers9

36

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

Community
  • 1
  • 1
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • Actually, you can programmatically access the properties no problem from Spring env for (MutablePropertySource propertySource : env.getPropertySources()) { // PropertySource propertySource = (PropertySource) element; if (propertySource instanceof MapPropertySource) { mps = (MapPropertySource) propertySource; – tom Nov 09 '18 at 07:53
  • 1
    @tom true, but that functionality was introduced several years after this answer :-) – Sean Patrick Floyd Nov 10 '18 at 01:54
9

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"/>
mattyboy
  • 91
  • 1
  • 2
8
@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;
Community
  • 1
  • 1
vahapt
  • 1,685
  • 1
  • 21
  • 26
  • 4
    This doesn't answer the question as stated. – Joel Nov 12 '15 at 19:17
  • Is that working with curren Mule ESB 3.7.3 runtime ? I can't reach a solution with that, it is giving me an error such as `Failed to convert property value of type 'java.util.ArrayList' to required type 'java.util.List' for property 'messageProcessors'` – Hayra Aug 18 '16 at 11:42
5

Spring follows Inversion Of Control approach, this means that we can simply inject particular property into POJO. But there are some cases, when you would like to access property given by name directly from your code - some might see it as anti-pattern - this is palpably true, but lets concentrate on how to do it.

The PropertiesAccessor below provides access to properties loaded by Property Placeholder and encapsulates container specific stuff. It also caches found properties because call on AbstractBeanFactory#resolveEmbeddedValue(String) is not cheap.

@Named 
public class PropertiesAccessor {

    private final AbstractBeanFactory beanFactory;

    private final Map<String,String> cache = new ConcurrentHashMap<>(); 

    @Inject 
    protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
        this.beanFactory = beanFactory; 
    } 

    public  String getProperty(String key) { 
        if(cache.containsKey(key)){ 
            return cache.get(key); 
        } 

        String foundProp = null; 
        try { 
            foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");        
            cache.put(key,foundProp);        
        } catch (IllegalArgumentException ex) { 
           // ok - property was not found 
        } 

        return foundProp; 
    } 
}
Maciej Miklas
  • 3,305
  • 4
  • 27
  • 52
2

Found answer at below site:

http://forum.spring.io/forum/spring-projects/container/106180-programmatic-access-to-properties-defined-for-the-propertyplaceholderconfigurer

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" id="propertyConfigurer">
<property name="properties" ref="props" />
</bean>
<bean id="props" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="location" value="file:C:/CONFIG/settings.properties"/>
</bean>
Sandeep
  • 29
  • 1
1
<util:properties id="prop" location="location of prop file" />

This return java.util.Properties object

In JAVA Code

Properties prop = (Properties) context.getBean("prop");

Now you can access ,

prop.getProperty("key");
Sarang
  • 422
  • 5
  • 11
1

This works if you need to scan multiple locations for your properties ...

<bean id="yourProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="locations">
        <array value-type="org.springframework.core.io.Resource">
            <value>classpath:yourProperties.properties</value>
            <value>file:../conf/yourProperties.properties</value>
            <value>file:conf/yourProperties.properties</value>
            <value>file:yourProperties.properties</value>
        </array>
    </property>
    <property name="ignoreResourceNotFound" value="true" />
</bean>
<context:property-placeholder properties-ref="yourProperties" ignore-unresolvable="true"/>

And then in your actual classes ...

@Autowired
Properties yourProperties;

Tested using Spring 5.1.4

StefanLe
  • 11
  • 3
0

Create beans for your properties before putting them in property-placeholder to make the properties easy to access in-code.

Ex:

<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="resources" value="classpath:META-INF/spring/config.properties" />
</bean>

<context:property-placeholder properties-ref="configProperties" ignore-unresolvable="true"/>

Code:

@Autowired
private PropertiesFactoryBean configProperties;

You can also use @Resource(name="configProperties")

TheJeff
  • 3,665
  • 34
  • 52
-1

Let's asume that you the properties file defined in that "parent" framework

<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="location" value="classpath:main.properties" />
</bean>

You can use the @Value annotation in this way:

@Value( value = "#{applicationProperties['my.app.property']}" )
private String myProperty;
Carlos D.
  • 415
  • 5
  • 8
  • 1
    It is faced with error in Spring 4: Indexing into type 'org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' is not supported – Mostafa Barmshory Sep 14 '17 at 05:46
  • Like @SeanPatrickFloyd commented: "_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**_." I hope this helps you: [Reading-valued-from-properties-file-at-Runtime](https://stackoverflow.com/questions/5665296/reading-valued-from-properties-file-at-runtime/5673678#5673678) – Carlos D. Oct 23 '17 at 13:59