diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java index 835b0087bc..5af02175f1 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java @@ -102,16 +102,16 @@ class JavaBeanBinder implements BeanBinder { private static Bean cached; - private final Class type; + private final ResolvableType type; - private final ResolvableType resolvableType; + private final Class resolvedType; private final Map properties = new LinkedHashMap<>(); - Bean(ResolvableType resolvableType, Class type) { - this.resolvableType = resolvableType; + Bean(ResolvableType type, Class resolvedType) { this.type = type; - putProperties(type); + this.resolvedType = resolvedType; + putProperties(resolvedType); } private void putProperties(Class type) { @@ -155,7 +155,7 @@ class JavaBeanBinder implements BeanBinder { } private BeanProperty getBeanProperty(String name) { - return new BeanProperty(name, this.resolvableType); + return new BeanProperty(name, this.type); } private void addField(Field field) { @@ -165,10 +165,6 @@ class JavaBeanBinder implements BeanBinder { } } - public Class getType() { - return this.type; - } - public Map getProperties() { return this.properties; } @@ -181,27 +177,36 @@ class JavaBeanBinder implements BeanBinder { instance = target.getValue().get(); } if (instance == null) { - instance = (T) BeanUtils.instantiateClass(this.type); + instance = (T) BeanUtils.instantiateClass(this.resolvedType); } return instance; }); } + private boolean isOfDifferentType(ResolvableType targetType) { + if (this.type.hasGenerics() || targetType.hasGenerics()) { + return !this.type.equals(targetType); + } + return this.resolvedType == null + || !this.resolvedType.equals(targetType.resolve()); + } + @SuppressWarnings("unchecked") public static Bean get(Bindable bindable, boolean canCallGetValue) { - Class type = bindable.getType().resolve(Object.class); + ResolvableType type = bindable.getType(); + Class resolvedType = type.resolve(Object.class); Supplier value = bindable.getValue(); T instance = null; if (canCallGetValue && value != null) { instance = value.get(); - type = (instance != null) ? instance.getClass() : type; + resolvedType = (instance != null) ? instance.getClass() : resolvedType; } - if (instance == null && !isInstantiable(type)) { + if (instance == null && !isInstantiable(resolvedType)) { return null; } Bean bean = Bean.cached; - if (bean == null || !type.equals(bean.getType())) { - bean = new Bean<>(bindable.getType(), type); + if (bean == null || bean.isOfDifferentType(type)) { + bean = new Bean<>(type, resolvedType); cached = bean; } return (Bean) bean; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java index 7944f9d76f..b818fde8b2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java @@ -494,6 +494,19 @@ public class JavaBeanBinderTests { assertThat(bean.getCounter()).isEqualTo(42); } + @Test + public void bindToClassShouldCacheWithGenerics() { + // gh-16821 + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo.integers[a].value", "1"); + source.put("foo.booleans[b].value", "true"); + this.sources.add(source); + ExampleWithGenericMap bean = this.binder + .bind("foo", Bindable.of(ExampleWithGenericMap.class)).get(); + assertThat(bean.getIntegers().get("a").getValue()).isEqualTo(1); + assertThat(bean.getBooleans().get("b").getValue()).isEqualTo(true); + } + public static class ExampleValueBean { private int intValue; @@ -914,4 +927,34 @@ public class JavaBeanBinderTests { } + public static class ExampleWithGenericMap { + + private final Map> integers = new LinkedHashMap<>(); + + private final Map> booleans = new LinkedHashMap<>(); + + public Map> getIntegers() { + return this.integers; + } + + public Map> getBooleans() { + return this.booleans; + } + + } + + public static class GenericValue { + + private T value; + + public T getValue() { + return this.value; + } + + public void setValue(T value) { + this.value = value; + } + + } + }