Skip to content

Commit 1e742aa

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 (cherry picked from commit 75da9c3)
1 parent 20dd585 commit 1e742aa

File tree

3 files changed

+49
-5
lines changed

3 files changed

+49
-5
lines changed

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

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -336,11 +336,10 @@ private static <C> Method[] getBaseTypeMethods(C context, Class<?> baseType) {
336336

337337
Method[] methods = baseTypeMethodsCache.get(baseType);
338338
if (methods == null) {
339-
boolean isInterface = baseType.isInterface();
340-
methods = isInterface ? baseType.getMethods() : ReflectionUtils.getDeclaredMethods(baseType);
339+
methods = ReflectionUtils.getDeclaredMethods(baseType);
341340
int cleared = 0;
342341
for (int i = 0; i < methods.length; i++) {
343-
if ((!isInterface && Modifier.isPrivate(methods[i].getModifiers())) ||
342+
if (Modifier.isPrivate(methods[i].getModifiers()) ||
344343
hasPlainJavaAnnotationsOnly(methods[i]) ||
345344
getDeclaredAnnotations(methods[i], false).length == 0) {
346345
methods[i] = null;

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

+34-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -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

+12
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)