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

1

9 Answers 9

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

Sign up to request clarification or add additional context in comments.

2 Comments

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 true, but that functionality was introduced several years after this answer :-)
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"/> 

Comments

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; 

2 Comments

This doesn't answer the question as stated.
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'
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; } } 

Comments

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> 

Comments

2

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

Comments

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"); 

Comments

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")

Comments

-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; 

2 Comments

It is faced with error in Spring 4: Indexing into type 'org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' is not supported
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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.