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 824f1ec5fa2..4baa849bc46 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 @@ -887,9 +887,12 @@ public class AnnotatedElementUtilsTests { @AliasFor(annotation = ContextConfig.class, attribute = "locations") String[] xmlFiles() default {}; - @AliasFor(annotation = ContextConfig.class, attribute = "locations") + // intentionally omitted: attribute = "locations" + @AliasFor(annotation = ContextConfig.class) String[] locations() default {}; + // TODO Determine why transitive implicit alias does not work + // hoping to be able to omit: attribute = "locations" @AliasFor(annotation = ContextConfig.class, attribute = "locations") String[] value() default {}; } 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 bf625f085ed..53327096962 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 @@ -778,6 +778,25 @@ public class AnnotationUtilsTests { assertThat(getAttributeAliasNames(groovyScript), containsInAnyOrder("xmlFile")); } + @Test + public void getAttributeAliasNamesFromComposedAnnotationWithImplicitAliasesWithImpliedAliasNamesOmitted() + throws Exception { + + Method value = ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("value"); + Method location = ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("location"); + Method xmlFile = ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("xmlFile"); + + // Meta-annotation attribute overrides + assertEquals("value", getAttributeOverrideName(value, ContextConfig.class)); + assertEquals("location", getAttributeOverrideName(location, ContextConfig.class)); + assertEquals("location", getAttributeOverrideName(xmlFile, ContextConfig.class)); + + // Implicit aliases + assertThat(getAttributeAliasNames(value), containsInAnyOrder("location", "xmlFile")); + assertThat(getAttributeAliasNames(location), containsInAnyOrder("value", "xmlFile")); + assertThat(getAttributeAliasNames(xmlFile), containsInAnyOrder("value", "location")); + } + @Test public void getAttributeAliasNamesFromComposedAnnotationWithTransitiveImplicitAliases() throws Exception { Method xml = TransitiveImplicitAliasesContextConfig.class.getDeclaredMethod("xml"); @@ -810,6 +829,26 @@ public class AnnotationUtilsTests { assertThat(getAttributeAliasNames(groovy), containsInAnyOrder("xml")); } + @Test + public void getAttributeAliasNamesFromComposedAnnotationWithTransitiveImplicitAliasesWithImpliedAliasNamesOmitted() + throws Exception { + + Method xml = TransitiveImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("xml"); + Method groovy = TransitiveImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("groovy"); + + // Meta-annotation attribute overrides + assertEquals("location", getAttributeOverrideName(xml, ContextConfig.class)); + assertEquals("location", getAttributeOverrideName(groovy, ContextConfig.class)); + + // Explicit meta-annotation attribute overrides + assertEquals("xmlFile", getAttributeOverrideName(xml, ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class)); + assertEquals("location", getAttributeOverrideName(groovy, ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class)); + + // Transitive implicit aliases + assertThat(getAttributeAliasNames(groovy), containsInAnyOrder("xml")); + assertThat(getAttributeAliasNames(xml), containsInAnyOrder("groovy")); + } + @Test public void synthesizeAnnotationWithoutAttributeAliases() throws Exception { Component component = WebController.class.getAnnotation(Component.class); @@ -992,6 +1031,31 @@ public class AnnotationUtilsTests { assertEquals("groovyScript: ", expected, synthesizedConfig.groovyScript()); } + @Test + public void synthesizeAnnotationWithImplicitAliasesWithImpliedAliasNamesOmitted() throws Exception { + assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted( + ValueImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "value"); + assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted( + LocationsImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "location"); + assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted( + XmlFilesImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "xmlFile"); + } + + private void assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(Class clazz, + String expected) throws Exception { + + ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig config = clazz.getAnnotation( + ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class); + assertNotNull(config); + + ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig synthesizedConfig = synthesizeAnnotation(config); + assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class)); + + assertEquals("value: ", expected, synthesizedConfig.value()); + assertEquals("locations: ", expected, synthesizedConfig.location()); + assertEquals("xmlFiles: ", expected, synthesizedConfig.xmlFile()); + } + @Test public void synthesizeAnnotationWithImplicitAliasesForAliasPair() throws Exception { Class clazz = ImplicitAliasesForAliasPairContextConfigClass.class; @@ -2113,6 +2177,48 @@ public class AnnotationUtilsTests { static class Location3ImplicitAliasesContextConfigClass { } + @ContextConfig + @Retention(RetentionPolicy.RUNTIME) + @interface ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig { + + // intentionally omitted: attribute = "value" + @AliasFor(annotation = ContextConfig.class) + String value() default ""; + + // intentionally omitted: attribute = "locations" + @AliasFor(annotation = ContextConfig.class) + String location() default ""; + + @AliasFor(annotation = ContextConfig.class, attribute = "location") + String xmlFile() default ""; + } + + @ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig + @Retention(RetentionPolicy.RUNTIME) + @interface TransitiveImplicitAliasesWithImpliedAliasNamesOmittedContextConfig { + + @AliasFor(annotation = ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class, attribute = "xmlFile") + String xml() default ""; + + @AliasFor(annotation = ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class, attribute = "location") + String groovy() default ""; + } + + // Attribute value intentionally matches attribute name: + @ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig("value") + static class ValueImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass { + } + + // Attribute value intentionally matches attribute name: + @ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig(location = "location") + static class LocationsImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass { + } + + // Attribute value intentionally matches attribute name: + @ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig(xmlFile = "xmlFile") + static class XmlFilesImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass { + } + @ContextConfig @Retention(RetentionPolicy.RUNTIME) @interface ImplicitAliasesWithMissingDefaultValuesContextConfig {