diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 74fac9d436..e8fef86db6 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1465,6 +1465,11 @@ public abstract class AnnotationUtils { boolean sameTargetDeclared = (sourceAnnotationType.equals(aliasedAnnotationType) || Annotation.class.equals(aliasedAnnotationType)); + // Explicit alias for a different target meta-annotation? + if (!searchWithinSameAnnotation && !targetAnnotationType.equals(aliasedAnnotationType)) { + return null; + } + // Wrong search scope? if (searchWithinSameAnnotation && !sameTargetDeclared) { return null; diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java index 7d7a6c1b0c..958173b365 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java @@ -491,6 +491,30 @@ public class AnnotatedElementUtilsTests { assertEquals("TX qualifier via synthesized annotation.", "aliasForQualifier", annotation.qualifier()); } + @Test + public void findMergedAnnotationForMultipleMetaAnnotationsWithClashingAttributeNames() { + final String[] xmlLocations = new String[] { "test.xml" }; + final String[] propFiles = new String[] { "test.properties" }; + + Class element = AliasedComposedContextConfigAndTestPropSourceClass.class; + + ContextConfig contextConfig = findMergedAnnotation(element, ContextConfig.class); + assertNotNull("@ContextConfig on " + element, contextConfig); + assertArrayEquals("locations", xmlLocations, contextConfig.locations()); + assertArrayEquals("value", xmlLocations, contextConfig.value()); + + // Synthesized annotation + TestPropSource testPropSource = AnnotationUtils.findAnnotation(element, TestPropSource.class); + assertArrayEquals("locations", propFiles, testPropSource.locations()); + assertArrayEquals("value", propFiles, testPropSource.value()); + + // Merged annotation + testPropSource = findMergedAnnotation(element, TestPropSource.class); + assertNotNull("@TestPropSource on " + element, testPropSource); + assertArrayEquals("locations", propFiles, testPropSource.locations()); + assertArrayEquals("value", propFiles, testPropSource.value()); + } + @Test public void findMergedAnnotationAttributesOnClassWithAttributeAliasInComposedAnnotationAndNestedAnnotationsInTargetAnnotation() { Class element = TestComponentScanClass.class; @@ -622,6 +646,19 @@ public class AnnotatedElementUtilsTests { @interface MetaAndLocalTxConfig { } + /** + * Mock of {@code org.springframework.test.context.TestPropertySource}. + */ + @Retention(RetentionPolicy.RUNTIME) + @interface TestPropSource { + + @AliasFor("locations") + String[] value() default {}; + + @AliasFor("value") + String[] locations() default {}; + } + /** * Mock of {@code org.springframework.test.context.ContextConfiguration}. */ @@ -679,6 +716,15 @@ public class AnnotatedElementUtilsTests { String[] xmlConfigFiles(); } + @ContextConfig(locations = "shadowed.xml") + @TestPropSource(locations = "test.properties") + @Retention(RetentionPolicy.RUNTIME) + @interface AliasedComposedContextConfigAndTestPropSource { + + @AliasFor(annotation = ContextConfig.class, attribute = "locations") + String[] xmlConfigFiles() default "default.xml"; + } + /** * Mock of {@code org.springframework.context.annotation.ComponentScan} */ @@ -855,6 +901,10 @@ public class AnnotatedElementUtilsTests { static class InvalidAliasedComposedContextConfigClass { } + @AliasedComposedContextConfigAndTestPropSource(xmlConfigFiles = "test.xml") + static class AliasedComposedContextConfigAndTestPropSourceClass { + } + @TestComponentScan(packages = "com.example.app.test") static class TestComponentScanClass { } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java index 6a3b3a024e..08519ece3d 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java @@ -675,6 +675,13 @@ public class AnnotationUtilsTests { assertThat(set.size(), is(0)); } + @Test + public void getAliasedAttributeNameFromWrongTargetAnnotation() throws Exception { + Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile"); + assertNull("xmlConfigFile is not an alias for @Component.", + getAliasedAttributeName(attribute, Component.class)); + } + @Test public void getAliasedAttributeNameFromAliasedComposedAnnotation() throws Exception { Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");