Browse Source

Select method with resolved return type match (among multiple candidates)

Removes unnecessary array type check for parameters of candidate methods.

Closes gh-35936
pull/35943/head
Juergen Hoeller 2 weeks ago
parent
commit
92e9543ad4
  1. 24
      spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java
  2. 77
      spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java

24
spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java

@ -29,7 +29,6 @@ import org.jspecify.annotations.Nullable; @@ -29,7 +29,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
* Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
@ -115,8 +114,8 @@ public final class BridgeMethodResolver { @@ -115,8 +114,8 @@ public final class BridgeMethodResolver {
if (bridgedMethod == null) {
// Gather all methods with matching name and parameter size.
List<Method> candidateMethods = new ArrayList<>();
MethodFilter filter = (candidateMethod -> isBridgedCandidateFor(candidateMethod, bridgeMethod));
ReflectionUtils.doWithMethods(userClass, candidateMethods::add, filter);
ReflectionUtils.doWithMethods(userClass, candidateMethods::add,
candidateMethod -> isBridgedCandidateFor(candidateMethod, bridgeMethod));
if (!candidateMethods.isEmpty()) {
bridgedMethod = (candidateMethods.size() == 1 ? candidateMethods.get(0) :
searchCandidates(candidateMethods, bridgeMethod, targetClass));
@ -152,9 +151,6 @@ public final class BridgeMethodResolver { @@ -152,9 +151,6 @@ public final class BridgeMethodResolver {
private static @Nullable Method searchCandidates(
List<Method> candidateMethods, Method bridgeMethod, Class<?> targetClass) {
if (candidateMethods.isEmpty()) {
return null;
}
Method previousMethod = null;
boolean sameSig = true;
for (Method candidateMethod : candidateMethods) {
@ -204,18 +200,16 @@ public final class BridgeMethodResolver { @@ -204,18 +200,16 @@ public final class BridgeMethodResolver {
}
private static boolean checkResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> clazz) {
// First, compare return type.
ResolvableType genericReturnType = ResolvableType.forMethodReturnType(genericMethod, clazz);
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateMethod.getReturnType()).equals(
ClassUtils.resolvePrimitiveIfNecessary(genericReturnType.toClass()))) {
return false;
}
Class<?>[] candidateParameters = candidateMethod.getParameterTypes();
for (int i = 0; i < candidateParameters.length; i++) {
ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, clazz);
Class<?> candidateParameter = candidateParameters[i];
if (candidateParameter.isArray()) {
// An array type: compare the component type.
if (!candidateParameter.componentType().equals(genericParameter.getComponentType().toClass())) {
return false;
}
}
// A non-array type: compare the type itself.
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameters[i]).equals(
ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
return false;
}

77
spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java

@ -44,17 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -44,17 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@SuppressWarnings("rawtypes")
class BridgeMethodResolverTests {
private static Method findMethodWithReturnType(String name, Class<?> returnType, Class<SettingsDaoImpl> targetType) {
Method[] methods = targetType.getMethods();
for (Method m : methods) {
if (m.getName().equals(name) && m.getReturnType().equals(returnType)) {
return m;
}
}
return null;
}
@Test
void findBridgedMethod() throws Exception {
Method unbridged = MyFoo.class.getDeclaredMethod("someMethod", String.class, Object.class);
@ -106,6 +95,24 @@ class BridgeMethodResolverTests { @@ -106,6 +95,24 @@ class BridgeMethodResolverTests {
assertThat(mostSpecificMethod).isSameAs(originalMethod);
}
@Test
void findBridgedMethodWithDefaultMethodInInterfaceHierarchy() throws Exception {
Method getValueDefault = DefaultMethods.class.getMethod("getValue");
Method getValuesDefault = DefaultMethods.class.getMethod("getValues");
Method getValueArgDefault = DefaultMethods.class.getMethod("getValue", Integer.class);
Method getValuesArgDefault = DefaultMethods.class.getMethod("getValues", Integer[].class);
for (Method method : ConcreteMethods.class.getMethods()) {
if (method.getName().equals("getValue")) {
assertThat(BridgeMethodResolver.findBridgedMethod(method)).isEqualTo(
method.getParameterCount() > 0 ? getValueArgDefault : getValueDefault);
}
else if (method.getName().equals("getValues")) {
assertThat(BridgeMethodResolver.findBridgedMethod(method)).isEqualTo(
method.getParameterCount() > 0 ? getValuesArgDefault : getValuesDefault);
}
}
}
@Test
void findBridgedMethodInHierarchyWithBoundedGenerics() throws Exception {
Method originalMethod = Bar.class.getDeclaredMethod("someMethod", Object.class, Object.class);
@ -161,6 +168,16 @@ class BridgeMethodResolverTests { @@ -161,6 +168,16 @@ class BridgeMethodResolverTests {
assertThat(BridgeMethodResolver.findBridgedMethod(loadWithSettingsReturn)).isEqualTo(method);
}
private static Method findMethodWithReturnType(String name, Class<?> returnType, Class<?> targetType) {
Method[] methods = targetType.getMethods();
for (Method m : methods) {
if (m.getName().equals(name) && m.getReturnType().equals(returnType)) {
return m;
}
}
return null;
}
@Test
void findBridgedMethodFromParent() throws Exception {
Method loadFromParentBridge = SettingsDaoImpl.class.getMethod("loadFromParent");
@ -451,6 +468,44 @@ class BridgeMethodResolverTests { @@ -451,6 +468,44 @@ class BridgeMethodResolverTests {
}
interface InterfaceMethods<T> {
T getValue();
T[] getValues();
T getValue(T arg);
T[] getValues(T[] args);
}
interface DefaultMethods extends InterfaceMethods<Integer> {
default Integer getValue() {
return 0;
}
default Integer[] getValues() {
return new Integer[0];
}
@Override
default Integer getValue(Integer arg) {
return 0;
}
@Override
default Integer[] getValues(Integer[] args) {
return new Integer[0];
}
}
static class ConcreteMethods implements DefaultMethods {
}
public static class Enclosing<T> {
public class Enclosed<S> {

Loading…
Cancel
Save