Ignore bridge methods when binding java beans

Update `JavaBeanBinder` so that bridge methods are ignored when binding.

Fixes gh-33105
pull/33276/head
Phillip Webb 2 years ago
parent e03c2e876a
commit 46acb6fb97

@ -24,7 +24,9 @@ import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
@ -34,6 +36,7 @@ import org.springframework.boot.context.properties.bind.Binder.Context;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertyState;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
@ -129,13 +132,22 @@ class JavaBeanBinder implements DataObjectBinder {
private void addProperties(Class<?> type) {
while (type != null && !Object.class.equals(type)) {
Method[] declaredMethods = getSorted(type, Class::getDeclaredMethods, Method::getName);
Method[] declaredMethods = getSorted(type, this::getDeclaredMethods, Method::getName);
Field[] declaredFields = getSorted(type, Class::getDeclaredFields, Field::getName);
addProperties(declaredMethods, declaredFields);
type = type.getSuperclass();
}
}
private Method[] getDeclaredMethods(Class<?> type) {
Method[] methods = type.getDeclaredMethods();
Set<Method> result = new LinkedHashSet<>(methods.length);
for (Method method : methods) {
result.add(BridgeMethodResolver.findBridgedMethod(method));
}
return result.toArray(new Method[0]);
}
private <S, E> E[] getSorted(S source, Function<S, E[]> elements, Function<E, String> name) {
E[] result = elements.apply(source);
Arrays.sort(result, Comparator.comparing(name));

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,6 +36,7 @@ import org.springframework.boot.context.properties.bind.handler.IgnoreErrorsBind
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MockConfigurationPropertySource;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.boot.convert.Delimiter;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
@ -585,6 +586,18 @@ class JavaBeanBinderTests {
assertThat(bean.getNames()).containsExactly("spring", "boot");
}
@Test // gh-33105
void bindWhenHasBridgeMethods() {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("test.value", "spring-boot");
this.sources.add(source);
ApplicationConversionService conversionService = new ApplicationConversionService();
conversionService.addConverter(String.class, BridgeType.class, BridgeType::new);
Binder binder = new Binder(this.sources, null, conversionService);
BridgeMethods bean = binder.bind("test", Bindable.of(BridgeMethods.class)).get();
assertThat(bean.getValue()).hasToString("spring-boot");
}
static class ExampleValueBean {
private int intValue;
@ -1178,4 +1191,46 @@ class JavaBeanBinderTests {
}
static class BridgeMethodsBase<T extends BridgeBaseType> {
private T value;
T getValue() {
return this.value;
}
void setValue(T value) {
this.value = value;
}
}
static class BridgeMethods extends BridgeMethodsBase<BridgeType> {
@Override
BridgeType getValue() {
return super.getValue();
}
}
static class BridgeBaseType {
}
static class BridgeType extends BridgeBaseType {
private String value;
BridgeType(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
}
}

Loading…
Cancel
Save