Browse Source

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
pull/855/merge
Sam Brannen 9 years ago
parent
commit
c8d604bf05
  1. 5
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
  2. 50
      spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java
  3. 7
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

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

@ -1465,6 +1465,11 @@ public abstract class AnnotationUtils { @@ -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;

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

@ -491,6 +491,30 @@ public class AnnotatedElementUtilsTests { @@ -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 { @@ -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 { @@ -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 { @@ -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 {
}

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

@ -675,6 +675,13 @@ public class AnnotationUtilsTests { @@ -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");

Loading…
Cancel
Save