Skip to content

Commit e03f3b8

Browse files
committed
Merge branch '2.1.x'
Closes gh-16857
2 parents 544e81c + f665910 commit e03f3b8

File tree

2 files changed

+64
-16
lines changed

2 files changed

+64
-16
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,16 @@ private static class Bean<T> {
102102

103103
private static Bean<?> cached;
104104

105-
private final Class<?> type;
105+
private final ResolvableType type;
106106

107-
private final ResolvableType resolvableType;
107+
private final Class<?> resolvedType;
108108

109109
private final Map<String, BeanProperty> properties = new LinkedHashMap<>();
110110

111-
Bean(ResolvableType resolvableType, Class<?> type) {
112-
this.resolvableType = resolvableType;
111+
Bean(ResolvableType type, Class<?> resolvedType) {
113112
this.type = type;
114-
putProperties(type);
113+
this.resolvedType = resolvedType;
114+
putProperties(resolvedType);
115115
}
116116

117117
private void putProperties(Class<?> type) {
@@ -155,7 +155,7 @@ private void addMethodIfPossible(Method method, String prefix, int parameterCoun
155155
}
156156

157157
private BeanProperty getBeanProperty(String name) {
158-
return new BeanProperty(name, this.resolvableType);
158+
return new BeanProperty(name, this.type);
159159
}
160160

161161
private void addField(Field field) {
@@ -165,10 +165,6 @@ private void addField(Field field) {
165165
}
166166
}
167167

168-
public Class<?> getType() {
169-
return this.type;
170-
}
171-
172168
public Map<String, BeanProperty> getProperties() {
173169
return this.properties;
174170
}
@@ -181,27 +177,36 @@ public BeanSupplier<T> getSupplier(Bindable<T> target) {
181177
instance = target.getValue().get();
182178
}
183179
if (instance == null) {
184-
instance = (T) BeanUtils.instantiateClass(this.type);
180+
instance = (T) BeanUtils.instantiateClass(this.resolvedType);
185181
}
186182
return instance;
187183
});
188184
}
189185

186+
private boolean isOfDifferentType(ResolvableType targetType) {
187+
if (this.type.hasGenerics() || targetType.hasGenerics()) {
188+
return !this.type.equals(targetType);
189+
}
190+
return this.resolvedType == null
191+
|| !this.resolvedType.equals(targetType.resolve());
192+
}
193+
190194
@SuppressWarnings("unchecked")
191195
public static <T> Bean<T> get(Bindable<T> bindable, boolean canCallGetValue) {
192-
Class<?> type = bindable.getType().resolve(Object.class);
196+
ResolvableType type = bindable.getType();
197+
Class<?> resolvedType = type.resolve(Object.class);
193198
Supplier<T> value = bindable.getValue();
194199
T instance = null;
195200
if (canCallGetValue && value != null) {
196201
instance = value.get();
197-
type = (instance != null) ? instance.getClass() : type;
202+
resolvedType = (instance != null) ? instance.getClass() : resolvedType;
198203
}
199-
if (instance == null && !isInstantiable(type)) {
204+
if (instance == null && !isInstantiable(resolvedType)) {
200205
return null;
201206
}
202207
Bean<?> bean = Bean.cached;
203-
if (bean == null || !type.equals(bean.getType())) {
204-
bean = new Bean<>(bindable.getType(), type);
208+
if (bean == null || bean.isOfDifferentType(type)) {
209+
bean = new Bean<>(type, resolvedType);
205210
cached = bean;
206211
}
207212
return (Bean<T>) bean;

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,19 @@ public void bindToClassShouldIgnoreStaticAccessors() {
494494
assertThat(bean.getCounter()).isEqualTo(42);
495495
}
496496

497+
@Test
498+
public void bindToClassShouldCacheWithGenerics() {
499+
// gh-16821
500+
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
501+
source.put("foo.integers[a].value", "1");
502+
source.put("foo.booleans[b].value", "true");
503+
this.sources.add(source);
504+
ExampleWithGenericMap bean = this.binder
505+
.bind("foo", Bindable.of(ExampleWithGenericMap.class)).get();
506+
assertThat(bean.getIntegers().get("a").getValue()).isEqualTo(1);
507+
assertThat(bean.getBooleans().get("b").getValue()).isEqualTo(true);
508+
}
509+
497510
public static class ExampleValueBean {
498511

499512
private int intValue;
@@ -914,4 +927,34 @@ public void setValue(Class<? extends Throwable> value) {
914927

915928
}
916929

930+
public static class ExampleWithGenericMap {
931+
932+
private final Map<String, GenericValue<Integer>> integers = new LinkedHashMap<>();
933+
934+
private final Map<String, GenericValue<Boolean>> booleans = new LinkedHashMap<>();
935+
936+
public Map<String, GenericValue<Integer>> getIntegers() {
937+
return this.integers;
938+
}
939+
940+
public Map<String, GenericValue<Boolean>> getBooleans() {
941+
return this.booleans;
942+
}
943+
944+
}
945+
946+
public static class GenericValue<T> {
947+
948+
private T value;
949+
950+
public T getValue() {
951+
return this.value;
952+
}
953+
954+
public void setValue(T value) {
955+
this.value = value;
956+
}
957+
958+
}
959+
917960
}

0 commit comments

Comments
 (0)