diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java index 3ebcefc8481..50fbc8fda31 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java @@ -271,10 +271,10 @@ final class AnnotationTypeMapping { int[] mappings = this.conventionMappings; for (int i = 0; i < mappings.length; i++) { String name = this.attributes.get(i).getName(); - MirrorSet mirrors = getMirrorSets().getAssigned(i); int mapped = rootAttributes.indexOf(name); if (!MergedAnnotation.VALUE.equals(name) && mapped != -1) { mappings[i] = mapped; + MirrorSet mirrors = getMirrorSets().getAssigned(i); if (mirrors != null) { for (int j = 0; j < mirrors.size(); j++) { mappings[mirrors.getAttributeIndex(j)] = mapped; @@ -509,7 +509,6 @@ final class AnnotationTypeMapping { * @return {@code true} if the value is equivalent to the default value */ boolean isEquivalentToDefaultValue(int attributeIndex, Object value, ValueExtractor valueExtractor) { - Method attribute = this.attributes.get(attributeIndex); return isEquivalentToDefaultValue(attribute, value, valueExtractor); } 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 67ca097531a..063d00440f6 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 @@ -207,6 +207,70 @@ class MergedAnnotationsTests { } + @Nested + class ConventionBasedAnnotationAttributeOverrideTests { + + @Test + void getWithInheritedAnnotationsAttributesWithConventionBasedComposedAnnotation() { + MergedAnnotation annotation = + MergedAnnotations.from(ConventionBasedComposedContextConfigurationClass.class, + SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); + assertThat(annotation.isPresent()).isTrue(); + assertThat(annotation.getStringArray("locations")).containsExactly("explicitDeclaration"); + assertThat(annotation.getStringArray("value")).containsExactly("explicitDeclaration"); + } + + @Test + void getWithInheritedAnnotationsFromHalfConventionBasedAndHalfAliasedComposedAnnotation1() { + // SPR-13554: convention mapping mixed with AliasFor annotations + // xmlConfigFiles can be used because it has an AliasFor annotation + MergedAnnotation annotation = + MergedAnnotations.from(HalfConventionBasedAndHalfAliasedComposedContextConfigurationClass1.class, + SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); + assertThat(annotation.getStringArray("locations")).containsExactly("explicitDeclaration"); + assertThat(annotation.getStringArray("value")).containsExactly("explicitDeclaration"); + } + + @Test + void withInheritedAnnotationsFromHalfConventionBasedAndHalfAliasedComposedAnnotation2() { + // SPR-13554: convention mapping mixed with AliasFor annotations + // locations doesn't apply because it has no AliasFor annotation + MergedAnnotation annotation = + MergedAnnotations.from(HalfConventionBasedAndHalfAliasedComposedContextConfigurationClass2.class, + SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); + assertThat(annotation.getStringArray("locations")).isEmpty(); + assertThat(annotation.getStringArray("value")).isEmpty(); + } + + @Test + void getWithInheritedAnnotationsFromInvalidConventionBasedComposedAnnotation() { + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> MergedAnnotations.from(InvalidConventionBasedComposedContextConfigurationClass.class, + SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class)); + } + + @Test + void getWithTypeHierarchyWithSingleElementOverridingAnArrayViaConvention() { + testGetWithTypeHierarchy(ConventionBasedSinglePackageComponentScanClass.class, "com.example.app.test"); + } + + @Test + void getWithTypeHierarchyWithLocalAliasesThatConflictWithAttributesInMetaAnnotationByConvention() { + MergedAnnotation annotation = + MergedAnnotations.from(SpringApplicationConfigurationClass.class, SearchStrategy.TYPE_HIERARCHY) + .get(ContextConfiguration.class); + assertThat(annotation.getStringArray("locations")).isEmpty(); + assertThat(annotation.getStringArray("value")).isEmpty(); + assertThat(annotation.getClassArray("classes")).containsExactly(Number.class); + } + + @Test + void getWithTypeHierarchyOnMethodWithSingleElementOverridingAnArrayViaConvention() throws Exception { + testGetWithTypeHierarchyWebMapping(WebController.class.getMethod("postMappedWithPathAttribute")); + } + + } + @Test void fromPreconditions() { SearchStrategy strategy = SearchStrategy.DIRECT; @@ -479,41 +543,7 @@ class MergedAnnotationsTests { assertThat(annotation.isPresent()).isTrue(); } - @Test - void getWithInheritedAnnotationsAttributesWithConventionBasedComposedAnnotation() { - MergedAnnotation annotation = MergedAnnotations.from( - ConventionBasedComposedContextConfigurationClass.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); - assertThat(annotation.isPresent()).isTrue(); - assertThat(annotation.getStringArray("locations")).containsExactly( - "explicitDeclaration"); - assertThat(annotation.getStringArray("value")).containsExactly( - "explicitDeclaration"); - } - @Test - void getWithInheritedAnnotationsFromHalfConventionBasedAndHalfAliasedComposedAnnotation1() { - // SPR-13554: convention mapping mixed with AliasFor annotations - // xmlConfigFiles can be used because it has an AliasFor annotation - MergedAnnotation annotation = MergedAnnotations.from( - HalfConventionBasedAndHalfAliasedComposedContextConfigurationClass1.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); - assertThat(annotation.getStringArray("locations")).containsExactly( - "explicitDeclaration"); - assertThat(annotation.getStringArray("value")).containsExactly( - "explicitDeclaration"); - } - - @Test - void withInheritedAnnotationsFromHalfConventionBasedAndHalfAliasedComposedAnnotation2() { - // SPR-13554: convention mapping mixed with AliasFor annotations - // locations doesn't apply because it has no AliasFor annotation - MergedAnnotation annotation = MergedAnnotations.from( - HalfConventionBasedAndHalfAliasedComposedContextConfigurationClass2.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); - assertThat(annotation.getStringArray("locations")).isEmpty(); - assertThat(annotation.getStringArray("value")).isEmpty(); - } @Test void withInheritedAnnotationsFromAliasedComposedAnnotation() { @@ -591,13 +621,6 @@ class MergedAnnotationsTests { assertThat(annotation.getClassArray("classes")).isEmpty(); } - @Test - void getWithInheritedAnnotationsFromInvalidConventionBasedComposedAnnotation() { - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotations.from(InvalidConventionBasedComposedContextConfigurationClass.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class)); - } - @Test void getWithInheritedAnnotationsFromShadowedAliasComposedAnnotation() { MergedAnnotation annotation = MergedAnnotations.from( @@ -760,11 +783,6 @@ class MergedAnnotationsTests { testGetWithTypeHierarchy(ComponentScanWithBasePackagesAndValueAliasClass.class, "com.example.app.test"); } - @Test - void getWithTypeHierarchyWithSingleElementOverridingAnArrayViaConvention() { - testGetWithTypeHierarchy(ConventionBasedSinglePackageComponentScanClass.class, "com.example.app.test"); - } - @Test void getWithTypeHierarchyWithSingleElementOverridingAnArrayViaAliasFor() { testGetWithTypeHierarchy(AliasForBasedSinglePackageComponentScanClass.class, "com.example.app.test"); @@ -793,22 +811,6 @@ class MergedAnnotationsTests { "test.properties"); } - @Test - void getWithTypeHierarchyWithLocalAliasesThatConflictWithAttributesInMetaAnnotationByConvention() { - MergedAnnotation annotation = MergedAnnotations.from( - SpringApplicationConfigurationClass.class, SearchStrategy.TYPE_HIERARCHY).get( - ContextConfiguration.class); - assertThat(annotation.getStringArray("locations")).isEmpty(); - assertThat(annotation.getStringArray("value")).isEmpty(); - assertThat(annotation.getClassArray("classes")).containsExactly(Number.class); - } - - @Test - void getWithTypeHierarchyOnMethodWithSingleElementOverridingAnArrayViaConvention() throws Exception { - testGetWithTypeHierarchyWebMapping( - WebController.class.getMethod("postMappedWithPathAttribute")); - } - @Test void getWithTypeHierarchyOnMethodWithSingleElementOverridingAnArrayViaAliasFor() throws Exception { testGetWithTypeHierarchyWebMapping(