Skip to content

Commit 75da9c3

Browse files
committed
Scan annotations on method in interface hierarchy only once
Prior to this commit, the AnnotationsScanner used in the MergedAnnotations infrastructure found duplicate annotations on methods within multi-level interface hierarchies. This commit addresses this issue by scanning methods at a given level in the interface hierarchy using ReflectionUtils#getDeclaredMethods instead of Class#getMethods, since the latter includes public methods declared in super-interfaces which will anyway be scanned when processing super-interfaces recursively. Closes gh-31803
1 parent 952223d commit 75da9c3

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,10 @@ private static <C> Method[] getBaseTypeMethods(C context, Class<?> baseType) {
334334

335335
Method[] methods = baseTypeMethodsCache.get(baseType);
336336
if (methods == null) {
337-
boolean isInterface = baseType.isInterface();
338-
methods = isInterface ? baseType.getMethods() : ReflectionUtils.getDeclaredMethods(baseType);
337+
methods = ReflectionUtils.getDeclaredMethods(baseType);
339338
int cleared = 0;
340339
for (int i = 0; i < methods.length; i++) {
341-
if ((!isInterface && Modifier.isPrivate(methods[i].getModifiers())) ||
340+
if (Modifier.isPrivate(methods[i].getModifiers()) ||
342341
hasPlainJavaAnnotationsOnly(methods[i]) ||
343342
getDeclaredAnnotations(methods[i], false).length == 0) {
344343
methods[i] = null;

spring-core/src/test/java/org/springframework/core/annotation/AnnotationsScannerTests.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,15 @@ void typeHierarchyStrategyOnMethodWhenHasInterfaceScansInterfaces() {
357357
Method source = methodFrom(WithSingleInterface.class);
358358
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly(
359359
"0:TestAnnotation1", "1:TestAnnotation2", "1:TestInheritedAnnotation2");
360+
361+
source = methodFrom(Hello1Impl.class);
362+
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly("1:TestAnnotation1");
363+
}
364+
365+
@Test // gh-31803
366+
void typeHierarchyStrategyOnMethodWhenHasInterfaceHierarchyScansInterfacesOnlyOnce() {
367+
Method source = methodFrom(Hello2Impl.class);
368+
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly("1:TestAnnotation1");
360369
}
361370

362371
@Test
@@ -691,6 +700,30 @@ public void method() {
691700
}
692701
}
693702

703+
interface Hello1 {
704+
705+
@TestAnnotation1
706+
void method();
707+
}
708+
709+
interface Hello2 extends Hello1 {
710+
}
711+
712+
static class Hello1Impl implements Hello1 {
713+
714+
@Override
715+
public void method() {
716+
}
717+
}
718+
719+
static class Hello2Impl implements Hello2 {
720+
721+
@Override
722+
public void method() {
723+
}
724+
}
725+
726+
694727
@TestAnnotation2
695728
@TestInheritedAnnotation2
696729
static class HierarchySuperclass extends HierarchySuperSuperclass {

spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import org.junit.jupiter.api.Test;
4141

4242
import org.springframework.core.Ordered;
43+
import org.springframework.core.annotation.AnnotationsScannerTests.Hello2Impl;
44+
import org.springframework.core.annotation.AnnotationsScannerTests.TestAnnotation1;
4345
import org.springframework.core.annotation.MergedAnnotation.Adapt;
4446
import org.springframework.core.annotation.MergedAnnotations.Search;
4547
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
@@ -686,6 +688,16 @@ void getWithTypeHierarchyInheritedFromInterfaceMethod() throws Exception {
686688
assertThat(annotation.getAggregateIndex()).isEqualTo(1);
687689
}
688690

691+
@Test // gh-31803
692+
void streamWithTypeHierarchyInheritedFromSuperInterfaceMethod() throws Exception {
693+
Method method = Hello2Impl.class.getMethod("method");
694+
long count = MergedAnnotations.search(SearchStrategy.TYPE_HIERARCHY)
695+
.from(method)
696+
.stream(TestAnnotation1.class)
697+
.count();
698+
assertThat(count).isEqualTo(1);
699+
}
700+
689701
@Test
690702
void getWithTypeHierarchyInheritedFromAbstractMethod() throws NoSuchMethodException {
691703
Method method = ConcreteClassWithInheritedAnnotation.class.getMethod("handle");

0 commit comments

Comments
 (0)