Skip to content

Commit c8d604b

Browse files
committed
Ensure @AliasFor overrides attribute in correct meta-annotation
Prior to this commit, an explicit override for an attribute in a meta-annotation configured via @AliasFor could potentially result in an incorrect override of an attribute of the same name but in the wrong meta-annotation. This commit fixes the algorithm in getAliasedAttributeName(Method, Class) in AnnotationUtils by ensuring that an explicit attribute override is only applied to the configured target meta-annotation (i.e., configured via the 'annotation' attribute in @AliasFor). Issue: SPR-13325
1 parent 1a659e3 commit c8d604b

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,6 +1465,11 @@ static String getAliasedAttributeName(Method attribute, Class<? extends Annotati
14651465
boolean sameTargetDeclared =
14661466
(sourceAnnotationType.equals(aliasedAnnotationType) || Annotation.class.equals(aliasedAnnotationType));
14671467

1468+
// Explicit alias for a different target meta-annotation?
1469+
if (!searchWithinSameAnnotation && !targetAnnotationType.equals(aliasedAnnotationType)) {
1470+
return null;
1471+
}
1472+
14681473
// Wrong search scope?
14691474
if (searchWithinSameAnnotation && !sameTargetDeclared) {
14701475
return null;

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,30 @@ public void findMergedAnnotationWithAttributeAliasesInTargetAnnotation() {
491491
assertEquals("TX qualifier via synthesized annotation.", "aliasForQualifier", annotation.qualifier());
492492
}
493493

494+
@Test
495+
public void findMergedAnnotationForMultipleMetaAnnotationsWithClashingAttributeNames() {
496+
final String[] xmlLocations = new String[] { "test.xml" };
497+
final String[] propFiles = new String[] { "test.properties" };
498+
499+
Class<?> element = AliasedComposedContextConfigAndTestPropSourceClass.class;
500+
501+
ContextConfig contextConfig = findMergedAnnotation(element, ContextConfig.class);
502+
assertNotNull("@ContextConfig on " + element, contextConfig);
503+
assertArrayEquals("locations", xmlLocations, contextConfig.locations());
504+
assertArrayEquals("value", xmlLocations, contextConfig.value());
505+
506+
// Synthesized annotation
507+
TestPropSource testPropSource = AnnotationUtils.findAnnotation(element, TestPropSource.class);
508+
assertArrayEquals("locations", propFiles, testPropSource.locations());
509+
assertArrayEquals("value", propFiles, testPropSource.value());
510+
511+
// Merged annotation
512+
testPropSource = findMergedAnnotation(element, TestPropSource.class);
513+
assertNotNull("@TestPropSource on " + element, testPropSource);
514+
assertArrayEquals("locations", propFiles, testPropSource.locations());
515+
assertArrayEquals("value", propFiles, testPropSource.value());
516+
}
517+
494518
@Test
495519
public void findMergedAnnotationAttributesOnClassWithAttributeAliasInComposedAnnotationAndNestedAnnotationsInTargetAnnotation() {
496520
Class<?> element = TestComponentScanClass.class;
@@ -622,6 +646,19 @@ static class MetaCycleAnnotatedClass {
622646
@interface MetaAndLocalTxConfig {
623647
}
624648

649+
/**
650+
* Mock of {@code org.springframework.test.context.TestPropertySource}.
651+
*/
652+
@Retention(RetentionPolicy.RUNTIME)
653+
@interface TestPropSource {
654+
655+
@AliasFor("locations")
656+
String[] value() default {};
657+
658+
@AliasFor("value")
659+
String[] locations() default {};
660+
}
661+
625662
/**
626663
* Mock of {@code org.springframework.test.context.ContextConfiguration}.
627664
*/
@@ -679,6 +716,15 @@ static class MetaCycleAnnotatedClass {
679716
String[] xmlConfigFiles();
680717
}
681718

719+
@ContextConfig(locations = "shadowed.xml")
720+
@TestPropSource(locations = "test.properties")
721+
@Retention(RetentionPolicy.RUNTIME)
722+
@interface AliasedComposedContextConfigAndTestPropSource {
723+
724+
@AliasFor(annotation = ContextConfig.class, attribute = "locations")
725+
String[] xmlConfigFiles() default "default.xml";
726+
}
727+
682728
/**
683729
* Mock of {@code org.springframework.context.annotation.ComponentScan}
684730
*/
@@ -855,6 +901,10 @@ static class AliasedValueComposedContextConfigClass {
855901
static class InvalidAliasedComposedContextConfigClass {
856902
}
857903

904+
@AliasedComposedContextConfigAndTestPropSource(xmlConfigFiles = "test.xml")
905+
static class AliasedComposedContextConfigAndTestPropSourceClass {
906+
}
907+
858908
@TestComponentScan(packages = "com.example.app.test")
859909
static class TestComponentScanClass {
860910
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,13 @@ public void getDeclaredRepeatableAnnotationsDeclaredOnSuperclass() {
675675
assertThat(set.size(), is(0));
676676
}
677677

678+
@Test
679+
public void getAliasedAttributeNameFromWrongTargetAnnotation() throws Exception {
680+
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
681+
assertNull("xmlConfigFile is not an alias for @Component.",
682+
getAliasedAttributeName(attribute, Component.class));
683+
}
684+
678685
@Test
679686
public void getAliasedAttributeNameFromAliasedComposedAnnotation() throws Exception {
680687
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");

0 commit comments

Comments
 (0)