Given the following:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import java.util.EnumMap;
import java.util.List;
@EnableConfigurationProperties(DemoApplication.ExampleProperties.class)
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
public enum Example {
ONE, TWO, THREE;
}
@ConfigurationProperties(prefix = "example")
record ExampleProperties(List<ExampleEntry> entries) {}
record ExampleEntry(String name, EnumMap<Example, String> map) {}
}
and configuration:
example:
entries:
- name: one
map:
ONE: one
TWO: two
THREE: three
Will result in the following exception:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'example.entries[0].map' to java.util.EnumMap<com.example.demo.DemoApplication$Example, java.lang.String>:
Property: example.entries[0].name
Value: "one"
Origin: class path resource [application.yml] - 3:13
Reason: java.lang.IllegalArgumentException: Cannot create EnumMap for unknown key type
Action:
Update your application's configuration
It works if I switch to Map instead of EnumMap:
- public record ExampleEntry(String name, EnumMap<Example, String> map) {}
+ public record ExampleEntry(String name, Map<Example, String> map) {}
But the keys (for my use case) are enums and it makes sense to use the specialized map implementation.
After some debugging, it looks to be an issue on Boot's side in ValueObjectBinder:
https://github.com/spring-projects/spring-boot/blob/3648f5f494e6b5bb010bb7f881eb748cbc0f8cc5/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java#L160
The key portion is lost which then fails Framework's check:
https://github.com/spring-projects/spring-framework/blob/5715b2a783b383c7cae214c2e37efb43f48d924f/spring-core/src/main/java/org/springframework/core/CollectionFactory.java#L319
Additionally, maybe related: https://github.com/spring-projects/spring-boot/issues/19156
Comment From: ciscoo
Simplified example ExampleProperties to a record.
Comment From: wilkinsona
Thanks for the report. There are two similar problems here. One's in ValueObjectBinder to which you've linked, but it will only occur if the EnumMap record component or constructor parameter is annotated with @DefaultValue. The other is in MapBinder.bindAggregate(ConfigurationPropertyName, Bindable<?>, AggregateElementBinder). I think it's the latter that's causing the failure in your case. Regardless, both need to be updated to handle an EnumMap by providing the key type to CollectionFactory.