Description of issue
As of spring boot version 3.4.0, empty maps are mapped to configuration properties classes as null where previously they were mapped as empty collections. I don't think this is expected behaviour as empty lists are mapped as empty collections and this is a change in behaviour since 3.3.
How to reproduce
Here's an example configuration properties class and some YAML config which I used to demonstrate the issue as described above.
Configuration class
@Configuration
@ConfigurationProperties(prefix = "app.collections")
public class Collections {
private Map<String, String> map;
private List<String> list;
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
@PostConstruct
public void init() {
System.out.println("Map: " + map);
System.out.println("List: " + list);
}
}
YAML Configuration
app:
collections:
map: {}
list: []
Example running under 3.3.0
Example running under 3.4.3
Rationale for mapping empty YAML collections to empty Java collections
This behaviour has previously been useful for us because we can annotate the properties class with @Validated
and the collections with @NotNull
to verify that the application configuration always contains an explicit value for the collection, even if it's an empty collection, which is no longer possible.
Comment From: wilkinsona
This change is intentional I'm afraid. Please see https://github.com/spring-projects/spring-boot/issues/44265 and the issues to which it links.
I'm not sure why you'd want to enforce the configuration of an empty map as, semantically, there should be little difference between a null
map and an empty map. See, for example, org.springframework.util.CollectionUtils.isEmpty(Map<?, ?>)
that treats the two the same. Given your example with a mutable @ConfigurationProperties
class, I would initialize the map
field with an empty map if you don't want callers of getMap()
to have to deal with null
. For immutable properties classes, you can use @DefaultValue
.
Comment From: rjwats
Thanks for the reply Andy, and sorry for raising a duplicate issue.
My rationale was more about being able to enforce external configuration of the map:
- A null indicates no configuration was supplied and should result in the application failing to start with a meaningful error
- An empty map indicates a deliberate configuration decision which the application accepts
I recognize is probably a bit of a niche case and we can work around it.