diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java index 2ab67bb0cb..3f47417c6b 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java @@ -140,6 +140,13 @@ public interface MergedAnnotation { @Nullable MergedAnnotation getParent(); + /** + * Get the root annotation, i.e. the {@link #getDepth() depth} {@code 0} + * annotation as directly declared on the source. + * @return the root annotation + */ + MergedAnnotation getRoot(); + /** * Determine if the specified attribute name has a non-default value when * compared to the annotation declaration. diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java index 0c780e1c23..70360c2db5 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java @@ -66,6 +66,11 @@ final class MissingMergedAnnotation extends AbstractMerged return null; } + @Override + public MergedAnnotation getRoot() { + return this; + } + @Override public int getDepth() { return -1; diff --git a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java index 645a66e2bc..124816a50d 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java @@ -171,6 +171,16 @@ final class TypeMappedAnnotation extends AbstractMergedAnn this.valueExtractor, this.aggregateIndex, this.resolvedRootMirrors); } + @Override + public MergedAnnotation getRoot() { + if (getDepth() == 0) { + return this; + } + AnnotationTypeMapping rootMapping = this.mapping.getRoot(); + return new TypeMappedAnnotation<>(rootMapping, this.source, this.rootAttributes, + this.valueExtractor, this.aggregateIndex, this.resolvedRootMirrors); + } + @Override public boolean hasDefaultValue(String attributeName) { int attributeIndex = getAttributeIndex(attributeName, true); diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index 9fb1e2b72e..6ae3a02f15 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -154,6 +154,22 @@ public class MergedAnnotationsTests { .isEqualTo(ComposedTransactionalComponent.class); } + @Test + public void getRootWhenNotDirect() { + MergedAnnotations annotations = MergedAnnotations.from(ComposedTransactionalComponentClass.class); + MergedAnnotation annotation = annotations.get(TransactionalComponent.class); + assertThat(annotation.getDepth()).isGreaterThan(0); + assertThat(annotation.getRoot().getType()).isEqualTo(ComposedTransactionalComponent.class); + } + + @Test + public void getRootWhenDirect() { + MergedAnnotations annotations = MergedAnnotations.from(ComposedTransactionalComponentClass.class); + MergedAnnotation annotation = annotations.get(ComposedTransactionalComponent.class); + assertThat(annotation.getDepth()).isEqualTo(0); + assertThat(annotation.getRoot()).isSameAs(annotation); + } + @Test public void collectMultiValueMapFromNonAnnotatedClass() { MultiValueMap map = MergedAnnotations.from( diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java index 8fee0117c0..d5c042666c 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java @@ -78,6 +78,11 @@ public class MissingMergedAnnotationTests { assertThat(this.missing.getParent()).isNull(); } + @Test + public void getRootReturnsEmptyAnnotation() { + assertThat(this.missing.getRoot()).isSameAs(this.missing); + } + @Test public void hasNonDefaultValueThrowsNoSuchElementException() { assertThatNoSuchElementException().isThrownBy(