Use generic type for binder cache comparisons
Update `JavaBeanBinder` so that previously cached beans are compared using full generic type information. Prior to this commit binding would fail if a class with the same resolved type, but different generics was in the cache. Fixes gh-16821
This commit is contained in:
parent
7407b226f2
commit
f665910cdb
|
@ -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<String, BeanProperty> 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<String, BeanProperty> 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 <T> Bean<T> get(Bindable<T> bindable, boolean canCallGetValue) {
|
||||
Class<?> type = bindable.getType().resolve(Object.class);
|
||||
ResolvableType type = bindable.getType();
|
||||
Class<?> resolvedType = type.resolve(Object.class);
|
||||
Supplier<T> 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<T>) bean;
|
||||
|
|
|
@ -492,6 +492,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;
|
||||
|
@ -912,4 +925,34 @@ public class JavaBeanBinderTests {
|
|||
|
||||
}
|
||||
|
||||
public static class ExampleWithGenericMap {
|
||||
|
||||
private final Map<String, GenericValue<Integer>> integers = new LinkedHashMap<>();
|
||||
|
||||
private final Map<String, GenericValue<Boolean>> booleans = new LinkedHashMap<>();
|
||||
|
||||
public Map<String, GenericValue<Integer>> getIntegers() {
|
||||
return this.integers;
|
||||
}
|
||||
|
||||
public Map<String, GenericValue<Boolean>> getBooleans() {
|
||||
return this.booleans;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class GenericValue<T> {
|
||||
|
||||
private T value;
|
||||
|
||||
public T getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void setValue(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue