Skip to content

Commit

Permalink
Scan annotations on method in interface hierarchy only once
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
sbrannen committed Dec 12, 2023
1 parent 20dd585 commit 1e742aa
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -336,11 +336,10 @@ private static <C> Method[] getBaseTypeMethods(C context, Class<?> baseType) {

Method[] methods = baseTypeMethodsCache.get(baseType);
if (methods == null) {
boolean isInterface = baseType.isInterface();
methods = isInterface ? baseType.getMethods() : ReflectionUtils.getDeclaredMethods(baseType);
methods = ReflectionUtils.getDeclaredMethods(baseType);
int cleared = 0;
for (int i = 0; i < methods.length; i++) {
if ((!isInterface && Modifier.isPrivate(methods[i].getModifiers())) ||
if (Modifier.isPrivate(methods[i].getModifiers()) ||
hasPlainJavaAnnotationsOnly(methods[i]) ||
getDeclaredAnnotations(methods[i], false).length == 0) {
methods[i] = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -357,6 +357,15 @@ void typeHierarchyStrategyOnMethodWhenHasInterfaceScansInterfaces() {
Method source = methodFrom(WithSingleInterface.class);
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly(
"0:TestAnnotation1", "1:TestAnnotation2", "1:TestInheritedAnnotation2");

source = methodFrom(Hello1Impl.class);
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly("1:TestAnnotation1");
}

@Test // gh-31803
void typeHierarchyStrategyOnMethodWhenHasInterfaceHierarchyScansInterfacesOnlyOnce() {
Method source = methodFrom(Hello2Impl.class);
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly("1:TestAnnotation1");
}

@Test
Expand Down Expand Up @@ -691,6 +700,30 @@ public void method() {
}
}

interface Hello1 {

@TestAnnotation1
void method();
}

interface Hello2 extends Hello1 {
}

static class Hello1Impl implements Hello1 {

@Override
public void method() {
}
}

static class Hello2Impl implements Hello2 {

@Override
public void method() {
}
}


@TestAnnotation2
@TestInheritedAnnotation2
static class HierarchySuperclass extends HierarchySuperSuperclass {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import org.junit.jupiter.api.Test;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationsScannerTests.Hello2Impl;
import org.springframework.core.annotation.AnnotationsScannerTests.TestAnnotation1;
import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.core.annotation.MergedAnnotations.Search;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
Expand Down Expand Up @@ -686,6 +688,16 @@ void getWithTypeHierarchyInheritedFromInterfaceMethod() throws Exception {
assertThat(annotation.getAggregateIndex()).isEqualTo(1);
}

@Test // gh-31803
void streamWithTypeHierarchyInheritedFromSuperInterfaceMethod() throws Exception {
Method method = Hello2Impl.class.getMethod("method");
long count = MergedAnnotations.search(SearchStrategy.TYPE_HIERARCHY)
.from(method)
.stream(TestAnnotation1.class)
.count();
assertThat(count).isEqualTo(1);
}

@Test
void getWithTypeHierarchyInheritedFromAbstractMethod() throws NoSuchMethodException {
Method method = ConcreteClassWithInheritedAnnotation.class.getMethod("handle");
Expand Down

0 comments on commit 1e742aa

Please sign in to comment.