From 46acb6fb97b1e743f0ab85dd5baf07ec586b50cf Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 15 Nov 2022 19:30:04 -0800 Subject: [PATCH] Ignore bridge methods when binding java beans Update `JavaBeanBinder` so that bridge methods are ignored when binding. Fixes gh-33105 --- .../properties/bind/JavaBeanBinder.java | 14 ++++- .../properties/bind/JavaBeanBinderTests.java | 57 ++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) 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 021e6abcd4..766b753927 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 @@ -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 result = new LinkedHashSet<>(methods.length); + for (Method method : methods) { + result.add(BridgeMethodResolver.findBridgedMethod(method)); + } + return result.toArray(new Method[0]); + } + private E[] getSorted(S source, Function elements, Function name) { E[] result = elements.apply(source); Arrays.sort(result, Comparator.comparing(name)); 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 c7482bd941..9ead5a9403 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 @@ -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 { + + private T value; + + T getValue() { + return this.value; + } + + void setValue(T value) { + this.value = value; + } + + } + + static class BridgeMethods extends BridgeMethodsBase { + + @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; + } + + } + }