How to fill HashMap from java property file with Spring @Value
From Spring 4.1.x ( I can't remember specific version though ), you can do something like
@Value("#{${your.properties.key.name}}")
private Map<String, String> myMap;
where your.properties.key.name in your properties file should be something like
your.properties.key.name={\
name1 : 100, \
name2 : 200 \
}
Just make sure that you should create PropertySourcesPlaceholderConfigurer bean to make it work both in your app and if you are writing any unit test code to test your code, otherwise ${...} placeholder for the property value won't work as expected and you'll see some weird SpringEL errors.
You can use the SPEL json-like syntax to write a simple map or a map of list in property file.
simple.map={'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}
map.of.list={\
'KEY1': {'value1','value2'}, \
'KEY2': {'value3','value4'}, \
'KEY3': {'value5'} \
}
I used \
for multiline property to enhance readability
Then, in Java, you can access and parse it automatically with @Value
like this.
@Value("#{${simple.map}}")
Map<String, String> simpleMap;
@Value("#{${map.of.list}}")
Map<String, List<String>> mapOfList;
Here with ${simple.map}
, @Value
gets the following String from the property file:
"{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}"
Then, it is evaluated as if it was inlined
@Value("#{{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}}")
You can learn more in the official documentation
Is it possible to use Spring @Value, to map values from properties file to the HashMap?
Yes, it is. With a little help of code and Spel.
Firstly, consider this singleton Spring-bean (you should scan it):
@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);
}
}
Note: PropertySplitter
class uses Splitter
utility from Guava. Please refer to its documentation for further details.
Then, in some bean of yours:
@Component
public class MyBean {
@Value("#{PropertySplitter.map('${service.expiration}')}")
Map<String, String> propertyAsMap;
}
And finally, the property:
service.expiration = name1:100,name2:20
It's not exactly what you've asked, because this PropertySplitter
works with one single property that is transformed into a Map
, but I think you could either switch to this way of specifying properties, or modify the PropertySplitter
code so that it matches the more hierarchical way you desire.