Skip to content

Commit 8289036

Browse files
committed
Throw exception if required meta-annotation is not present
It is a configuration error if an alias is declared via @AliasFor for an attribute in a meta-annotation and the meta-annotation is not meta-present. However, prior to this commit, the support for validating the configuration of @AliasFor in AnnotationUtils currently silently ignored such errors. This commit fixes this by throwing an AnnotationConfigurationException whenever a required meta-annotation is not present or meta-present on an annotation that declares an explicit alias for an attribute in the meta-annotation. Issue: SPR-13335
1 parent 78ff4ff commit 8289036

File tree

3 files changed

+47
-13
lines changed

3 files changed

+47
-13
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AliasFor.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,18 @@
6565
* <li>Aliased attributes must declare the same return type.</li>
6666
* <li>Aliased attributes must declare a default value.</li>
6767
* <li>Aliased attributes must declare the same default value.</li>
68-
* <li>The {@link #annotation} attribute may remain set to the default,
69-
* although setting it to the declaring class for both attributes in the
70-
* pair is also valid.</li>
68+
* <li>The {@link #annotation} attribute should remain set to the default.</li>
7169
* </ol>
7270
* </li>
7371
* <li><strong>Alias for attribute in meta-annotation</strong>:
7472
* <ol>
7573
* <li>The attribute that is an alias for an attribute in a meta-annotation
76-
* must be annotated with {@code @AliasFor}; the {@link #attribute} must
77-
* reference the aliased attribute in the meta-annotation; and the
78-
* {@link #annotation} must reference the meta-annotation.</li>
74+
* must be annotated with {@code @AliasFor}, and the {@link #attribute} must
75+
* reference the aliased attribute in the meta-annotation.</li>
7976
* <li>Aliased attributes must declare the same return type.</li>
77+
* <li>The {@link #annotation} must reference the meta-annotation.</li>
78+
* <li>The referenced meta-annotation must be <em>meta-present</em> on the
79+
* annotation class that declares {@code @AliasFor}.</li>
8080
* </ol>
8181
* </li>
8282
* </ul>

spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,7 @@ static String getAliasedAttributeName(Method attribute, Class<? extends Annotati
14511451
Assert.isTrue(!Annotation.class.equals(targetAnnotationType),
14521452
"targetAnnotationType must not be java.lang.annotation.Annotation");
14531453

1454+
String attributeName = attribute.getName();
14541455
AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);
14551456

14561457
// Nothing to check
@@ -1470,12 +1471,6 @@ static String getAliasedAttributeName(Method attribute, Class<? extends Annotati
14701471
return null;
14711472
}
14721473

1473-
// Wrong search scope?
1474-
if (searchWithinSameAnnotation && !sameTargetDeclared) {
1475-
return null;
1476-
}
1477-
1478-
String attributeName = attribute.getName();
14791474
String aliasedAttributeName = getAliasedAttributeName(aliasFor, attribute);
14801475

14811476
if (!StringUtils.hasText(aliasedAttributeName)) {
@@ -1485,10 +1480,25 @@ static String getAliasedAttributeName(Method attribute, Class<? extends Annotati
14851480
throw new AnnotationConfigurationException(msg);
14861481
}
14871482

1488-
if (sameTargetDeclared) {
1483+
if (!sameTargetDeclared) {
1484+
// Target annotation is not meta-present?
1485+
if (findAnnotation(sourceAnnotationType, aliasedAnnotationType) == null) {
1486+
String msg = String.format("@AliasFor declaration on attribute [%s] in annotation [%s] declares "
1487+
+ "an alias for attribute [%s] in meta-annotation [%s] which is not meta-present.",
1488+
attributeName, sourceAnnotationType.getName(), aliasedAttributeName,
1489+
aliasedAnnotationType.getName());
1490+
throw new AnnotationConfigurationException(msg);
1491+
}
1492+
}
1493+
else {
14891494
aliasedAnnotationType = sourceAnnotationType;
14901495
}
14911496

1497+
// Wrong search scope?
1498+
if (searchWithinSameAnnotation && !sameTargetDeclared) {
1499+
return null;
1500+
}
1501+
14921502
Method aliasedAttribute;
14931503
try {
14941504
aliasedAttribute = aliasedAnnotationType.getDeclaredMethod(aliasedAttributeName);

spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,18 @@ public void synthesizeAnnotationWithAttributeAliasForAttributeWithDifferentDefau
831831
synthesizeAnnotation(annotation);
832832
}
833833

834+
@Test
835+
public void synthesizeAnnotationWithAttributeAliasForMetaAnnotationThatIsNotMetaPresent() throws Exception {
836+
AliasedComposedContextConfigNotMetaPresent annotation = AliasedComposedContextConfigNotMetaPresentClass.class.getAnnotation(AliasedComposedContextConfigNotMetaPresent.class);
837+
exception.expect(AnnotationConfigurationException.class);
838+
exception.expectMessage(startsWith("@AliasFor declaration on attribute [xmlConfigFile] in annotation"));
839+
exception.expectMessage(containsString(AliasedComposedContextConfigNotMetaPresent.class.getName()));
840+
exception.expectMessage(containsString("declares an alias for attribute [locations] in meta-annotation"));
841+
exception.expectMessage(containsString(ContextConfig.class.getName()));
842+
exception.expectMessage(endsWith("which is not meta-present."));
843+
synthesizeAnnotation(annotation);
844+
}
845+
834846
@Test
835847
public void synthesizeAnnotationWithAttributeAliases() throws Exception {
836848
Method method = WebController.class.getMethod("handleMappedWithValueAttribute");
@@ -1661,6 +1673,18 @@ static class AliasForWithMissingDefaultValuesClass {
16611673
static class AliasForAttributeWithDifferentDefaultValueClass {
16621674
}
16631675

1676+
// @ContextConfig --> Intentionally NOT meta-present
1677+
@Retention(RetentionPolicy.RUNTIME)
1678+
@interface AliasedComposedContextConfigNotMetaPresent {
1679+
1680+
@AliasFor(annotation = ContextConfig.class, attribute = "locations")
1681+
String xmlConfigFile();
1682+
}
1683+
1684+
@AliasedComposedContextConfigNotMetaPresent(xmlConfigFile = "test.xml")
1685+
static class AliasedComposedContextConfigNotMetaPresentClass {
1686+
}
1687+
16641688
@ContextConfig
16651689
@Retention(RetentionPolicy.RUNTIME)
16661690
@interface AliasedComposedContextConfig {

0 commit comments

Comments
 (0)