When constructor for ConfigurationProperties
bean has wildcard parameters, Boot doesn't apply converters, but instead keeps String as-is.
For example, if parameter is Map<String, ? extends List<? extends InetAddress>>
(this is how Kotlin translates List<String, Map<InetAddress>>
into Java bytecode), Boot will pass into constructor a map of lists of strings, while it should have converted those strings into InetAddress
es.
Reproducer:
package net.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.context.ConfigurableApplicationContext;
import java.net.InetAddress;
import java.util.List;
import java.util.Map;
@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(ExampleApplication.class, args);
ExampleProperties properties = ctx.getBean(ExampleProperties.class);
// OK.
System.out.println(properties.getAddresses1().get("localhost").get(0).getHostAddress());
// ClassCastException: class java.lang.String cannot be cast to class java.net.InetAddress
System.out.println(properties.getAddresses2().get("localhost").get(0).getHostAddress());
}
@ConstructorBinding
@ConfigurationProperties("example")
public static class ExampleProperties {
private final Map<String, List<InetAddress>> addresses1;
private final Map<String, ? extends List<? extends InetAddress>> addresses2;
public ExampleProperties(Map<String, List<InetAddress>> addresses1, Map<String, ? extends List<? extends InetAddress>> addresses2) {
this.addresses1 = addresses1;
this.addresses2 = addresses2;
}
public Map<String, List<InetAddress>> getAddresses1() {
return addresses1;
}
public Map<String, ? extends List<? extends InetAddress>> getAddresses2() {
return addresses2;
}
}
}
example:
addresses1:
localhost:
- 127.0.0.1
addresses2:
localhost:
- 127.0.0.1
Comment From: snicoll
Thanks for the sample. I've edited your title to clarify the issue is not related to constructor binding (I can reproduce with regular JavaBean accessors as well).
Comment From: mbhave
This is a bug in Spring Framework's ResolvableType
class. I'll leave this issue open so that we can add a test for this case in MapBinderTests
.
Comment From: philwebb
See https://github.com/spring-projects/spring-framework/pull/24145