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 bab16eaeda..f788127cc7 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 @@ -182,9 +182,9 @@ final class AnnotationTypeMapping { Method mirror = resolveAliasTarget(target, targetAliasFor, false); if (!mirror.equals(attribute)) { throw new AnnotationConfigurationException(String.format( - "%s must be declared as an @AliasFor '%s', not '%s'.", + "%s must be declared as an @AliasFor %s, not %s.", StringUtils.capitalize(AttributeMethods.describe(target)), - attribute.getName(), mirror.getName())); + AttributeMethods.describe(attribute), AttributeMethods.describe(mirror))); } } } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationTypeMappingsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationTypeMappingsTests.java index abe1a19bc0..f5cb39ec6d 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationTypeMappingsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationTypeMappingsTests.java @@ -160,11 +160,28 @@ class AnnotationTypeMappingsTests { @Test void forAnnotationTypeWhenAliasForToSelfAnnotatedToOtherAttribute() { - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - AnnotationTypeMappings.forAnnotationType(AliasForToSelfAnnotatedToOtherAttribute.class)) - .withMessage("Attribute 'b' in annotation [" - + AliasForToSelfAnnotatedToOtherAttribute.class.getName() - + "] must be declared as an @AliasFor 'a', not 'c'."); + String annotationType = AliasForToSelfAnnotatedToOtherAttribute.class.getName(); + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> AnnotationTypeMappings.forAnnotationType(AliasForToSelfAnnotatedToOtherAttribute.class)) + .withMessage("Attribute 'b' in annotation [" + annotationType + + "] must be declared as an @AliasFor attribute 'a' in annotation [" + annotationType + + "], not attribute 'c' in annotation [" + annotationType + "]."); + } + + @Test + void forAnnotationTypeWhenAliasForHasMixedImplicitAndExplicitAliases() { + assertMixedImplicitAndExplicitAliases(AliasForWithMixedImplicitAndExplicitAliasesV1.class, "b"); + assertMixedImplicitAndExplicitAliases(AliasForWithMixedImplicitAndExplicitAliasesV2.class, "a"); + } + + private void assertMixedImplicitAndExplicitAliases(Class annotationType, String overriddenAttribute) { + String annotationName = annotationType.getName(); + String metaAnnotationName = AliasPair.class.getName(); + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> AnnotationTypeMappings.forAnnotationType(annotationType)) + .withMessage("Attribute 'b' in annotation [" + annotationName + + "] must be declared as an @AliasFor attribute 'a' in annotation [" + annotationName + + "], not attribute '" + overriddenAttribute + "' in annotation [" + metaAnnotationName + "]."); } @Test @@ -641,6 +658,42 @@ class AnnotationTypeMappingsTests { String c() default ""; } + @Retention(RetentionPolicy.RUNTIME) + @interface AliasPair { + + @AliasFor("b") + String a() default ""; + + @AliasFor("a") + String b() default ""; + } + + @Retention(RetentionPolicy.RUNTIME) + @AliasPair + @interface AliasForWithMixedImplicitAndExplicitAliasesV1 { + + // attempted implicit alias via attribute override + @AliasFor(annotation = AliasPair.class, attribute = "b") + String b() default ""; + + // explicit local alias + @AliasFor("b") + String a() default ""; + } + + @Retention(RetentionPolicy.RUNTIME) + @AliasPair + @interface AliasForWithMixedImplicitAndExplicitAliasesV2 { + + // attempted implicit alias via attribute override + @AliasFor(annotation = AliasPair.class, attribute = "a") + String b() default ""; + + // explicit local alias + @AliasFor("b") + String a() default ""; + } + @Retention(RetentionPolicy.RUNTIME) @interface AliasForNonMetaAnnotated { @@ -712,16 +765,6 @@ class AnnotationTypeMappingsTests { String alias() default ""; } - @Retention(RetentionPolicy.RUNTIME) - @interface AliasPair { - - @AliasFor("b") - String a() default ""; - - @AliasFor("a") - String b() default ""; - } - @Retention(RetentionPolicy.RUNTIME) @interface ImplicitMirrorsTarget {