diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java index ed9ed52530..519133a623 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java @@ -18,6 +18,7 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -38,6 +39,7 @@ import org.springframework.util.StringUtils; * interface-declared parameter annotations from the concrete target method. * * @author Juergen Hoeller + * @author Sam Brannen * @since 6.1 * @see #getMethodAnnotation(Class) * @see #getMethodParameters() @@ -181,7 +183,7 @@ public class AnnotatedMethod { clazz = null; } if (clazz != null) { - for (Method candidate : clazz.getMethods()) { + for (Method candidate : clazz.getDeclaredMethods()) { if (isOverrideFor(candidate)) { parameterAnnotations.add(candidate.getParameterAnnotations()); } @@ -194,8 +196,9 @@ public class AnnotatedMethod { } private boolean isOverrideFor(Method candidate) { - if (!candidate.getName().equals(this.method.getName()) || - candidate.getParameterCount() != this.method.getParameterCount()) { + if (Modifier.isPrivate(candidate.getModifiers()) || + !candidate.getName().equals(this.method.getName()) || + (candidate.getParameterCount() != this.method.getParameterCount())) { return false; } Class[] paramTypes = this.method.getParameterTypes(); diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedMethodTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedMethodTests.java index 1118239d61..c2c073e624 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedMethodTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedMethodTests.java @@ -19,12 +19,15 @@ package org.springframework.core.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import org.junit.jupiter.api.Test; import org.springframework.core.MethodParameter; -import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.joining; import static org.assertj.core.api.Assertions.assertThat; /** @@ -55,6 +58,12 @@ class AnnotatedMethodTests { @Test void shouldFindAnnotationOnMethodParameterInGenericAbstractSuperclass() { + // Prerequisites for gh-35349 + Method abstractMethod = ReflectionUtils.findMethod(GenericAbstractSuperclass.class, "processTwo", Object.class); + assertThat(abstractMethod).isNotNull(); + assertThat(Modifier.isAbstract(abstractMethod.getModifiers())).as("abstract").isTrue(); + assertThat(Modifier.isPublic(abstractMethod.getModifiers())).as("public").isFalse(); + Method processTwo = getMethod("processTwo", String.class); AnnotatedMethod annotatedMethod = new AnnotatedMethod(processTwo); @@ -78,7 +87,14 @@ class AnnotatedMethodTests { private static Method getMethod(String name, Class...parameterTypes) { - return ClassUtils.getMethod(GenericInterfaceImpl.class, name, parameterTypes); + Class clazz = GenericInterfaceImpl.class; + Method method = ReflectionUtils.findMethod(clazz, name, parameterTypes); + if (method == null) { + String parameterNames = stream(parameterTypes).map(Class::getName).collect(joining(", ")); + throw new IllegalStateException("Expected method not found: %s#%s(%s)" + .formatted(clazz.getSimpleName(), name, parameterNames)); + } + return method; } @@ -103,13 +119,14 @@ class AnnotatedMethodTests { } @Handler - public abstract void processTwo(@Param C value); + // Intentionally NOT public + abstract void processTwo(@Param C value); } static class GenericInterfaceImpl extends GenericAbstractSuperclass { @Override - public void processTwo(String value) { + void processTwo(String value) { } }