Injecting List, Array, Set, and Map from Spring Configuration Files Using @Value and EL Expressions
This article explains how to store and inject collection types such as List, array, Set, and Map in Spring configuration files, demonstrates the pitfalls of using @Value directly, and provides practical solutions using @ConfigurationProperties, EL split functions, and custom decoders for flexible and safe property binding.
In everyday development you often need to store collection types like List or Map in configuration files; Spring natively supports these types, for example a test: section with a list in a .yml file or indexed entries in a .properties file.
Attempting to read such values directly with @Value("${test.list}") results in an IllegalArgumentException: Could not resolve placeholder 'test.list' because Spring cannot bind a collection to a simple placeholder.
The recommended fix is to create a configuration class annotated with @ConfigurationProperties("test") that defines a private List list; field, provides getters and setters, and then autowire this class wherever the list is needed.
While this removes the placeholder error, it tightly couples the code to the configuration class; any new key requires changes to the class.
Using arrays offers a simpler alternative: define comma‑separated values in the config (e.g., array1: aaa,bbb,ccc ) and inject them with @Value("${test.array1}") private String[] testArray1; . Adding a default value after a colon (e.g., ${test.array1:} ) prevents errors when the key is missing, though the array length becomes zero.
For Lists, you can keep the concise configuration and use an EL expression to split the string: @Value("#{'${test.list}'.split(',')}") private List<String> testList; . Adding a default value and an emptiness check ( #{'${test.list:}'.empty ? null : '${test.list:}'.split(',')} ) avoids a non‑empty default string.
Sets are handled similarly, with the EL expression automatically removing duplicates: @Value("#{'${test.set:}'.empty ? null : '${test.set:}'.split(',')}") private Set<Integer> testSet; .
Maps require the value to be a JSON string; you can inject them with @Value("#{${test.map1}}") private Map<String, String> map1; . When a key may be absent, a custom decoder is needed. The article provides a MapDecoder class using FastJSON and shows how to call it from @Value("#{T(com.github.jitwxs.demo.MapDecoder).decodeMap('${test.map1:}')}") .
Finally, note that @Value cannot be used together with @AllArgsConstructor , and long EL expressions can become hard to read, so consider readability when choosing a binding strategy.
Top Architecture Tech Stack
Sharing Java and Python tech insights, with occasional practical development tool tips.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.