Complete migration of annotation utility methods

Delete `InternalAnnotationUtils` and `InternalAnnotatedElementUtils` and
migrate exclusively to the new `MergedAnnotations` API.

Closes gh-22562
This commit is contained in:
Phillip Webb
2019-03-09 22:43:12 -08:00
committed by Juergen Hoeller
parent a14bfe9a21
commit 37255afca4
14 changed files with 366 additions and 6097 deletions

View File

@@ -70,12 +70,10 @@ public class AnnotationIntrospectionFailureTests {
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(
withExampleMetaAnnotation,
exampleMetaAnnotationClass)).isNull();
// Ideally hasAnnotation should return false, however, it currently
// detects annotations that might ultimately not load
assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation,
exampleAnnotationClass)).isTrue();
exampleAnnotationClass)).isFalse();
assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation,
exampleMetaAnnotationClass)).isTrue();
exampleMetaAnnotationClass)).isFalse();
}
@Test

View File

@@ -38,7 +38,6 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;
@@ -487,8 +486,8 @@ public class AnnotationUtilsTests {
@Test
public void getAnnotationAttributesWithAttributeAliasesWithDifferentValues() throws Exception {
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(containsString("attribute 'value' and its alias 'path'"));
exception.expectMessage(containsString("values of [{/enigma}] and [{/test}]"));
exception.expectMessage(containsString("attribute 'path' and its alias 'value'"));
exception.expectMessage(containsString("values of [{/test}] and [{/enigma}]"));
Method method = WebController.class.getMethod("handleMappedWithDifferentPathAndValueAttributes");
WebMapping webMapping = method.getAnnotation(WebMapping.class);
@@ -730,381 +729,6 @@ public class AnnotationUtilsTests {
assertThat(set.size(), is(0));
}
@Test
public void getAttributeOverrideNameFromWrongTargetAnnotation() throws Exception {
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
assertThat("xmlConfigFile is not an alias for @Component.",
InternalAnnotationUtils.getAttributeOverrideName(attribute, Component.class), is(nullValue()));
}
@Test
public void getAttributeOverrideNameForNonAliasedAttribute() throws Exception {
Method nonAliasedAttribute = ImplicitAliasesContextConfig.class.getDeclaredMethod("nonAliasedAttribute");
assertThat(InternalAnnotationUtils.getAttributeOverrideName(nonAliasedAttribute, ContextConfig.class), is(nullValue()));
}
@Test
public void getAttributeOverrideNameFromAliasedComposedAnnotation() throws Exception {
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(attribute, ContextConfig.class));
}
@Test
public void getAttributeAliasNamesFromComposedAnnotationWithImplicitAliases() throws Exception {
Method xmlFile = ImplicitAliasesContextConfig.class.getDeclaredMethod("xmlFile");
Method groovyScript = ImplicitAliasesContextConfig.class.getDeclaredMethod("groovyScript");
Method value = ImplicitAliasesContextConfig.class.getDeclaredMethod("value");
Method location1 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location1");
Method location2 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location2");
Method location3 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location3");
// Meta-annotation attribute overrides
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(xmlFile, ContextConfig.class));
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(groovyScript, ContextConfig.class));
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(value, ContextConfig.class));
// Implicit aliases
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xmlFile), containsInAnyOrder("value", "groovyScript", "location1", "location2", "location3"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(groovyScript), containsInAnyOrder("value", "xmlFile", "location1", "location2", "location3"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(value), containsInAnyOrder("xmlFile", "groovyScript", "location1", "location2", "location3"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(location1), containsInAnyOrder("xmlFile", "groovyScript", "value", "location2", "location3"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(location2), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location3"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(location3), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location2"));
}
@Test
public void getAttributeAliasNamesFromComposedAnnotationWithImplicitAliasesForAliasPair() throws Exception {
Method xmlFile = ImplicitAliasesForAliasPairContextConfig.class.getDeclaredMethod("xmlFile");
Method groovyScript = ImplicitAliasesForAliasPairContextConfig.class.getDeclaredMethod("groovyScript");
// Meta-annotation attribute overrides
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(xmlFile, ContextConfig.class));
assertEquals("value", InternalAnnotationUtils.getAttributeOverrideName(groovyScript, ContextConfig.class));
// Implicit aliases
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xmlFile), containsInAnyOrder("groovyScript"));
assertThat(InternalAnnotationUtils.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", InternalAnnotationUtils.getAttributeOverrideName(value, ContextConfig.class));
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(location, ContextConfig.class));
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(xmlFile, ContextConfig.class));
// Implicit aliases
assertThat(InternalAnnotationUtils.getAttributeAliasNames(value), containsInAnyOrder("location", "xmlFile"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(location), containsInAnyOrder("value", "xmlFile"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xmlFile), containsInAnyOrder("value", "location"));
}
@Test
public void getAttributeAliasNamesFromComposedAnnotationWithTransitiveImplicitAliases() throws Exception {
Method xml = TransitiveImplicitAliasesContextConfig.class.getDeclaredMethod("xml");
Method groovy = TransitiveImplicitAliasesContextConfig.class.getDeclaredMethod("groovy");
// Explicit meta-annotation attribute overrides
assertEquals("xmlFile", InternalAnnotationUtils.getAttributeOverrideName(xml, ImplicitAliasesContextConfig.class));
assertEquals("groovyScript", InternalAnnotationUtils.getAttributeOverrideName(groovy, ImplicitAliasesContextConfig.class));
// Transitive meta-annotation attribute overrides
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(xml, ContextConfig.class));
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(groovy, ContextConfig.class));
// Transitive implicit aliases
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xml), containsInAnyOrder("groovy"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(groovy), containsInAnyOrder("xml"));
}
@Test
public void getAttributeAliasNamesFromComposedAnnotationWithTransitiveImplicitAliasesForAliasPair() throws Exception {
Method xml = TransitiveImplicitAliasesForAliasPairContextConfig.class.getDeclaredMethod("xml");
Method groovy = TransitiveImplicitAliasesForAliasPairContextConfig.class.getDeclaredMethod("groovy");
// Explicit meta-annotation attribute overrides
assertEquals("xmlFile", InternalAnnotationUtils.getAttributeOverrideName(xml, ImplicitAliasesForAliasPairContextConfig.class));
assertEquals("groovyScript", InternalAnnotationUtils.getAttributeOverrideName(groovy, ImplicitAliasesForAliasPairContextConfig.class));
// Transitive implicit aliases
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xml), containsInAnyOrder("groovy"));
assertThat(InternalAnnotationUtils.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", InternalAnnotationUtils.getAttributeOverrideName(xml, ContextConfig.class));
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(groovy, ContextConfig.class));
// Explicit meta-annotation attribute overrides
assertEquals("xmlFile", InternalAnnotationUtils.getAttributeOverrideName(xml, ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class));
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(groovy, ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class));
// Transitive implicit aliases
assertThat(InternalAnnotationUtils.getAttributeAliasNames(groovy), containsInAnyOrder("xml"));
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xml), containsInAnyOrder("groovy"));
}
@Test
public void synthesizeAnnotationWithoutAttributeAliases() throws Exception {
Component component = WebController.class.getAnnotation(Component.class);
assertNotNull(component);
Component synthesizedComponent = InternalAnnotationUtils.synthesizeAnnotation(component);
assertNotNull(synthesizedComponent);
assertSame(component, synthesizedComponent);
assertEquals("value attribute: ", "webController", synthesizedComponent.value());
}
@Test
public void synthesizeAlreadySynthesizedAnnotation() throws Exception {
Method method = WebController.class.getMethod("handleMappedWithValueAttribute");
WebMapping webMapping = method.getAnnotation(WebMapping.class);
assertNotNull(webMapping);
WebMapping synthesizedWebMapping = InternalAnnotationUtils.synthesizeAnnotation(webMapping);
assertNotSame(webMapping, synthesizedWebMapping);
WebMapping synthesizedAgainWebMapping = InternalAnnotationUtils.synthesizeAnnotation(synthesizedWebMapping);
assertThat(synthesizedAgainWebMapping, instanceOf(SynthesizedAnnotation.class));
assertSame(synthesizedWebMapping, synthesizedAgainWebMapping);
assertEquals("name attribute: ", "foo", synthesizedAgainWebMapping.name());
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedAgainWebMapping.path());
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedAgainWebMapping.value());
}
@Test
public void synthesizeAnnotationWhereAliasForIsMissingAttributeDeclaration() throws Exception {
AliasForWithMissingAttributeDeclaration annotation = AliasForWithMissingAttributeDeclarationClass.class.getAnnotation(AliasForWithMissingAttributeDeclaration.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("@AliasFor declaration on attribute 'foo' in annotation"));
exception.expectMessage(containsString(AliasForWithMissingAttributeDeclaration.class.getName()));
exception.expectMessage(containsString("points to itself"));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWhereAliasForHasDuplicateAttributeDeclaration() throws Exception {
AliasForWithDuplicateAttributeDeclaration annotation = AliasForWithDuplicateAttributeDeclarationClass.class.getAnnotation(AliasForWithDuplicateAttributeDeclaration.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("In @AliasFor declared on attribute 'foo' in annotation"));
exception.expectMessage(containsString(AliasForWithDuplicateAttributeDeclaration.class.getName()));
exception.expectMessage(anyOf(
containsString("attribute 'attribute' and its alias 'value' are present with values of [baz] and [bar]"),
containsString("attribute 'attribute' and its alias 'value' are present with values of 'bar' and 'baz'")));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliasForNonexistentAttribute() throws Exception {
AliasForNonexistentAttribute annotation = AliasForNonexistentAttributeClass.class.getAnnotation(AliasForNonexistentAttribute.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(either(allOf(
startsWith("Attribute 'foo' in"),
containsString(AliasForNonexistentAttribute.class.getName()),
containsString("is declared as an @AliasFor nonexistent attribute 'bar'"))
).or(
containsString("@AliasFor declaration on attribute 'foo' in annotation ["
+ AliasForNonexistentAttribute.class.getName()
+ "] declares an alias for 'bar' which is not present")));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliasWithoutMirroredAliasFor() throws Exception {
AliasForWithoutMirroredAliasFor annotation =
AliasForWithoutMirroredAliasForClass.class.getAnnotation(AliasForWithoutMirroredAliasFor.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(allOf(startsWith("Attribute 'bar' in"),
containsString(AliasForWithoutMirroredAliasFor.class.getName()),
either(containsString("@AliasFor [foo]")).or(containsString("@AliasFor 'foo'"))));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliasWithMirroredAliasForWrongAttribute() throws Exception {
AliasForWithMirroredAliasForWrongAttribute annotation =
AliasForWithMirroredAliasForWrongAttributeClass.class.getAnnotation(AliasForWithMirroredAliasForWrongAttribute.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("Attribute 'bar' in"));
exception.expectMessage(containsString(AliasForWithMirroredAliasForWrongAttribute.class.getName()));
exception.expectMessage(anyOf(
containsString("must be declared as an @AliasFor [foo], not [quux]"),
containsString("is declared as an @AliasFor nonexistent attribute 'quux'"),
containsString("must be declared as an @AliasFor 'foo', not attribute 'quux'")));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliasForAttributeOfDifferentType() throws Exception {
AliasForAttributeOfDifferentType annotation =
AliasForAttributeOfDifferentTypeClass.class.getAnnotation(AliasForAttributeOfDifferentType.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("Misconfigured aliases"));
exception.expectMessage(containsString(AliasForAttributeOfDifferentType.class.getName()));
exception.expectMessage(containsString("attribute 'foo'"));
exception.expectMessage(containsString("attribute 'bar'"));
exception.expectMessage(containsString("same return type"));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliasForWithMissingDefaultValues() throws Exception {
AliasForWithMissingDefaultValues annotation =
AliasForWithMissingDefaultValuesClass.class.getAnnotation(AliasForWithMissingDefaultValues.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("Misconfigured aliases"));
exception.expectMessage(containsString(AliasForWithMissingDefaultValues.class.getName()));
exception.expectMessage(containsString("attribute 'foo' in annotation"));
exception.expectMessage(containsString("attribute 'bar' in annotation"));
exception.expectMessage(containsString("default values"));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliasForAttributeWithDifferentDefaultValue() throws Exception {
AliasForAttributeWithDifferentDefaultValue annotation =
AliasForAttributeWithDifferentDefaultValueClass.class.getAnnotation(AliasForAttributeWithDifferentDefaultValue.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("Misconfigured aliases"));
exception.expectMessage(containsString(AliasForAttributeWithDifferentDefaultValue.class.getName()));
exception.expectMessage(containsString("attribute 'foo' in annotation"));
exception.expectMessage(containsString("attribute 'bar' in annotation"));
exception.expectMessage(containsString("same default value"));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliasForMetaAnnotationThatIsNotMetaPresent() throws Exception {
AliasedComposedContextConfigNotMetaPresent annotation =
AliasedComposedContextConfigNotMetaPresentClass.class.getAnnotation(AliasedComposedContextConfigNotMetaPresent.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("@AliasFor declaration on attribute 'xmlConfigFile' in annotation"));
exception.expectMessage(containsString(AliasedComposedContextConfigNotMetaPresent.class.getName()));
exception.expectMessage(containsString("declares an alias for attribute 'location'"));
exception.expectMessage(containsString(ContextConfig.class.getName()));
exception.expectMessage(containsString("not meta-present"));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliases() throws Exception {
Method method = WebController.class.getMethod("handleMappedWithValueAttribute");
WebMapping webMapping = method.getAnnotation(WebMapping.class);
assertNotNull(webMapping);
WebMapping synthesizedWebMapping1 = InternalAnnotationUtils.synthesizeAnnotation(webMapping);
assertThat(synthesizedWebMapping1, instanceOf(SynthesizedAnnotation.class));
assertNotSame(webMapping, synthesizedWebMapping1);
assertEquals("name attribute: ", "foo", synthesizedWebMapping1.name());
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedWebMapping1.path());
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedWebMapping1.value());
WebMapping synthesizedWebMapping2 = InternalAnnotationUtils.synthesizeAnnotation(webMapping);
assertThat(synthesizedWebMapping2, instanceOf(SynthesizedAnnotation.class));
assertNotSame(webMapping, synthesizedWebMapping2);
assertEquals("name attribute: ", "foo", synthesizedWebMapping2.name());
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedWebMapping2.path());
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedWebMapping2.value());
}
@Test
public void synthesizeAnnotationWithImplicitAliases() throws Exception {
assertAnnotationSynthesisWithImplicitAliases(ValueImplicitAliasesContextConfigClass.class, "value");
assertAnnotationSynthesisWithImplicitAliases(Location1ImplicitAliasesContextConfigClass.class, "location1");
assertAnnotationSynthesisWithImplicitAliases(XmlImplicitAliasesContextConfigClass.class, "xmlFile");
assertAnnotationSynthesisWithImplicitAliases(GroovyImplicitAliasesContextConfigClass.class, "groovyScript");
}
private void assertAnnotationSynthesisWithImplicitAliases(Class<?> clazz, String expected) throws Exception {
ImplicitAliasesContextConfig config = clazz.getAnnotation(ImplicitAliasesContextConfig.class);
assertNotNull(config);
ImplicitAliasesContextConfig synthesizedConfig = InternalAnnotationUtils.synthesizeAnnotation(config);
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
assertEquals("value: ", expected, synthesizedConfig.value());
assertEquals("location1: ", expected, synthesizedConfig.location1());
assertEquals("xmlFile: ", expected, synthesizedConfig.xmlFile());
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) {
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig config = clazz.getAnnotation(
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class);
assertNotNull(config);
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig synthesizedConfig = InternalAnnotationUtils.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;
ImplicitAliasesForAliasPairContextConfig config = clazz.getAnnotation(ImplicitAliasesForAliasPairContextConfig.class);
assertNotNull(config);
ImplicitAliasesForAliasPairContextConfig synthesizedConfig = InternalAnnotationUtils.synthesizeAnnotation(config);
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
assertEquals("xmlFile: ", "test.xml", synthesizedConfig.xmlFile());
assertEquals("groovyScript: ", "test.xml", synthesizedConfig.groovyScript());
}
@Test
public void synthesizeAnnotationWithTransitiveImplicitAliases() throws Exception {
Class<?> clazz = TransitiveImplicitAliasesContextConfigClass.class;
TransitiveImplicitAliasesContextConfig config = clazz.getAnnotation(TransitiveImplicitAliasesContextConfig.class);
assertNotNull(config);
TransitiveImplicitAliasesContextConfig synthesizedConfig = InternalAnnotationUtils.synthesizeAnnotation(config);
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
assertEquals("xml: ", "test.xml", synthesizedConfig.xml());
assertEquals("groovy: ", "test.xml", synthesizedConfig.groovy());
}
@Test
public void synthesizeAnnotationWithTransitiveImplicitAliasesForAliasPair() throws Exception {
Class<?> clazz = TransitiveImplicitAliasesForAliasPairContextConfigClass.class;
TransitiveImplicitAliasesForAliasPairContextConfig config = clazz.getAnnotation(TransitiveImplicitAliasesForAliasPairContextConfig.class);
assertNotNull(config);
TransitiveImplicitAliasesForAliasPairContextConfig synthesizedConfig = InternalAnnotationUtils.synthesizeAnnotation(config);
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
assertEquals("xml: ", "test.xml", synthesizedConfig.xml());
assertEquals("groovy: ", "test.xml", synthesizedConfig.groovy());
}
@Test
public void synthesizeAnnotationWithImplicitAliasesWithMissingDefaultValues() throws Exception {
Class<?> clazz = ImplicitAliasesWithMissingDefaultValuesContextConfigClass.class;
@@ -1245,13 +869,6 @@ public class AnnotationUtilsTests {
assertEquals("location: ", "", contextConfig.location());
}
@Test
public void synthesizeAnnotationWithAttributeAliasesWithDifferentValues() throws Exception {
exception.expect(AnnotationConfigurationException.class);
ContextConfig contextConfig = InternalAnnotationUtils.synthesizeAnnotation(ContextConfigMismatch.class.getAnnotation(ContextConfig.class));
getValue(contextConfig);
}
@Test
public void synthesizeAnnotationFromMapWithMinimalAttributesWithAttributeAliases() throws Exception {
Map<String, Object> map = Collections.singletonMap("location", "test.xml");
@@ -1325,16 +942,9 @@ public class AnnotationUtilsTests {
public void synthesizeAnnotationFromMapWithAttributeOfIncorrectType() throws Exception {
Map<String, Object> map = Collections.singletonMap(VALUE, 42L);
exception.expect(IllegalArgumentException.class);
exception.expectMessage(either(allOf(startsWith("Attributes map"),
containsString("returned a value of type [java.lang.Long]"),
containsString("for attribute 'value'"),
containsString("but a value of type [java.lang.String] is required"),
containsString("as defined by annotation type ["
+ Component.class.getName() + "]"))).or(
containsString("Attribute 'value' in annotation "
+ Component.class.getName()
+ " should be of type "
+ "java.lang.String but a java.lang.Long value was returned")));
exception.expectMessage(containsString(
"Attribute 'value' in annotation org.springframework.stereotype.Component "
+ "should be compatible with java.lang.String but a java.lang.Long value was returned"));
synthesizeAnnotation(map, Component.class, null);
}
@@ -1359,210 +969,6 @@ public class AnnotationUtilsTests {
assertEquals("value from synthesized component: ", "webController", synthesizedComponent.value());
}
@Test
public void toStringForSynthesizedAnnotations() throws Exception {
Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");
WebMapping webMappingWithAliases = methodWithPath.getAnnotation(WebMapping.class);
assertNotNull(webMappingWithAliases);
Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes");
WebMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(WebMapping.class);
assertNotNull(webMappingWithPathAndValue);
WebMapping synthesizedWebMapping1 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
assertNotNull(synthesizedWebMapping1);
WebMapping synthesizedWebMapping2 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
assertNotNull(synthesizedWebMapping2);
assertThat(webMappingWithAliases.toString(), is(not(synthesizedWebMapping1.toString())));
assertToStringForWebMappingWithPathAndValue(synthesizedWebMapping1);
assertToStringForWebMappingWithPathAndValue(synthesizedWebMapping2);
}
private void assertToStringForWebMappingWithPathAndValue(WebMapping webMapping) {
String string = webMapping.toString();
assertThat(string, startsWith("@" + WebMapping.class.getName() + "("));
assertThat(string, containsString("value=[/test]"));
assertThat(string, containsString("path=[/test]"));
assertThat(string, containsString("name=bar"));
assertThat(string, containsString("method="));
assertThat(string, containsString("[GET, POST]"));
assertThat(string, endsWith(")"));
}
@Test
public void equalsForSynthesizedAnnotations() throws Exception {
Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");
WebMapping webMappingWithAliases = methodWithPath.getAnnotation(WebMapping.class);
assertNotNull(webMappingWithAliases);
Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes");
WebMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(WebMapping.class);
assertNotNull(webMappingWithPathAndValue);
WebMapping synthesizedWebMapping1 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
assertNotNull(synthesizedWebMapping1);
WebMapping synthesizedWebMapping2 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
assertNotNull(synthesizedWebMapping2);
// Equality amongst standard annotations
assertThat(webMappingWithAliases, is(webMappingWithAliases));
assertThat(webMappingWithPathAndValue, is(webMappingWithPathAndValue));
// Inequality amongst standard annotations
assertThat(webMappingWithAliases, is(not(webMappingWithPathAndValue)));
assertThat(webMappingWithPathAndValue, is(not(webMappingWithAliases)));
// Equality amongst synthesized annotations
assertThat(synthesizedWebMapping1, is(synthesizedWebMapping1));
assertThat(synthesizedWebMapping2, is(synthesizedWebMapping2));
assertThat(synthesizedWebMapping1, is(synthesizedWebMapping2));
assertThat(synthesizedWebMapping2, is(synthesizedWebMapping1));
// Equality between standard and synthesized annotations
assertThat(synthesizedWebMapping1, is(webMappingWithPathAndValue));
assertThat(webMappingWithPathAndValue, is(synthesizedWebMapping1));
// Inequality between standard and synthesized annotations
assertThat(synthesizedWebMapping1, is(not(webMappingWithAliases)));
assertThat(webMappingWithAliases, is(not(synthesizedWebMapping1)));
}
@Test
public void hashCodeForSynthesizedAnnotations() throws Exception {
Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");
WebMapping webMappingWithAliases = methodWithPath.getAnnotation(WebMapping.class);
assertNotNull(webMappingWithAliases);
Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes");
WebMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(WebMapping.class);
assertNotNull(webMappingWithPathAndValue);
WebMapping synthesizedWebMapping1 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
assertNotNull(synthesizedWebMapping1);
WebMapping synthesizedWebMapping2 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
assertNotNull(synthesizedWebMapping2);
// Equality amongst standard annotations
assertThat(webMappingWithAliases.hashCode(), is(webMappingWithAliases.hashCode()));
assertThat(webMappingWithPathAndValue.hashCode(), is(webMappingWithPathAndValue.hashCode()));
// Inequality amongst standard annotations
assertThat(webMappingWithAliases.hashCode(), is(not(webMappingWithPathAndValue.hashCode())));
assertThat(webMappingWithPathAndValue.hashCode(), is(not(webMappingWithAliases.hashCode())));
// Equality amongst synthesized annotations
assertThat(synthesizedWebMapping1.hashCode(), is(synthesizedWebMapping1.hashCode()));
assertThat(synthesizedWebMapping2.hashCode(), is(synthesizedWebMapping2.hashCode()));
assertThat(synthesizedWebMapping1.hashCode(), is(synthesizedWebMapping2.hashCode()));
assertThat(synthesizedWebMapping2.hashCode(), is(synthesizedWebMapping1.hashCode()));
// Equality between standard and synthesized annotations
assertThat(synthesizedWebMapping1.hashCode(), is(webMappingWithPathAndValue.hashCode()));
assertThat(webMappingWithPathAndValue.hashCode(), is(synthesizedWebMapping1.hashCode()));
// Inequality between standard and synthesized annotations
assertThat(synthesizedWebMapping1.hashCode(), is(not(webMappingWithAliases.hashCode())));
assertThat(webMappingWithAliases.hashCode(), is(not(synthesizedWebMapping1.hashCode())));
}
/**
* Fully reflection-based test that verifies support for
* {@linkplain AnnotationUtils#synthesizeAnnotation synthesizing annotations}
* across packages with non-public visibility of user types (e.g., a non-public
* annotation that uses {@code @AliasFor}).
*/
@Test
@SuppressWarnings("unchecked")
public void synthesizeNonPublicAnnotationWithAttributeAliasesFromDifferentPackage() throws Exception {
Class<?> clazz =
ClassUtils.forName("org.springframework.core.annotation.subpackage.NonPublicAliasedAnnotatedClass", null);
Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
ClassUtils.forName("org.springframework.core.annotation.subpackage.NonPublicAliasedAnnotation", null);
Annotation annotation = clazz.getAnnotation(annotationType);
assertNotNull(annotation);
Annotation synthesizedAnnotation = InternalAnnotationUtils.synthesizeAnnotation(annotation);
assertNotSame(annotation, synthesizedAnnotation);
assertNotNull(synthesizedAnnotation);
assertEquals("name attribute: ", "test", getValue(synthesizedAnnotation, "name"));
assertEquals("aliased path attribute: ", "/test", getValue(synthesizedAnnotation, "path"));
assertEquals("aliased path attribute: ", "/test", getValue(synthesizedAnnotation, "value"));
}
@Test
public void synthesizeAnnotationWithAttributeAliasesInNestedAnnotations() throws Exception {
List<String> expectedLocations = asList("A", "B");
Hierarchy hierarchy = ConfigHierarchyTestCase.class.getAnnotation(Hierarchy.class);
assertNotNull(hierarchy);
Hierarchy synthesizedHierarchy = InternalAnnotationUtils.synthesizeAnnotation(hierarchy);
assertNotSame(hierarchy, synthesizedHierarchy);
assertThat(synthesizedHierarchy, instanceOf(SynthesizedAnnotation.class));
ContextConfig[] configs = synthesizedHierarchy.value();
assertNotNull(configs);
assertTrue("nested annotations must be synthesized",
stream(configs).allMatch(c -> c instanceof SynthesizedAnnotation));
List<String> locations = stream(configs).map(ContextConfig::location).collect(toList());
assertThat(locations, is(expectedLocations));
List<String> values = stream(configs).map(ContextConfig::value).collect(toList());
assertThat(values, is(expectedLocations));
}
@Test
public void synthesizeAnnotationWithArrayOfAnnotations() throws Exception {
List<String> expectedLocations = asList("A", "B");
Hierarchy hierarchy = ConfigHierarchyTestCase.class.getAnnotation(Hierarchy.class);
assertNotNull(hierarchy);
Hierarchy synthesizedHierarchy = InternalAnnotationUtils.synthesizeAnnotation(hierarchy);
assertThat(synthesizedHierarchy, instanceOf(SynthesizedAnnotation.class));
ContextConfig contextConfig = SimpleConfigTestCase.class.getAnnotation(ContextConfig.class);
assertNotNull(contextConfig);
ContextConfig[] configs = synthesizedHierarchy.value();
List<String> locations = stream(configs).map(ContextConfig::location).collect(toList());
assertThat(locations, is(expectedLocations));
// Alter array returned from synthesized annotation
configs[0] = contextConfig;
// Re-retrieve the array from the synthesized annotation
configs = synthesizedHierarchy.value();
List<String> values = stream(configs).map(ContextConfig::value).collect(toList());
assertThat(values, is(expectedLocations));
}
@Test
public void synthesizeAnnotationWithArrayOfChars() throws Exception {
CharsContainer charsContainer = GroupOfCharsClass.class.getAnnotation(CharsContainer.class);
assertNotNull(charsContainer);
CharsContainer synthesizedCharsContainer = InternalAnnotationUtils.synthesizeAnnotation(charsContainer);
assertThat(synthesizedCharsContainer, instanceOf(SynthesizedAnnotation.class));
char[] chars = synthesizedCharsContainer.chars();
assertArrayEquals(new char[] { 'x', 'y', 'z' }, chars);
// Alter array returned from synthesized annotation
chars[0] = '?';
// Re-retrieve the array from the synthesized annotation
chars = synthesizedCharsContainer.chars();
assertArrayEquals(new char[] { 'x', 'y', 'z' }, chars);
}
@Test
public void interfaceWithAnnotatedMethods() {
assertTrue(InternalAnnotationUtils.getAnnotatedMethodsInBaseType(NonAnnotatedInterface.class).isEmpty());
assertFalse(InternalAnnotationUtils.getAnnotatedMethodsInBaseType(AnnotatedInterface.class).isEmpty());
assertTrue(InternalAnnotationUtils.getAnnotatedMethodsInBaseType(NullableAnnotatedInterface.class).isEmpty());
}
@SafeVarargs
static <T> T[] asArray(T... arr) {

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
/**
* Unit tests for {@link DefaultAnnotationAttributeExtractor}.
*
* @author Sam Brannen
* @since 4.2.1
*/
public class DefaultAnnotationAttributeExtractorTests extends AbstractAliasAwareAnnotationAttributeExtractorTestCase {
@Override
protected AnnotationAttributeExtractor<?> createExtractorFor(Class<?> clazz, String expected, Class<? extends Annotation> annotationType) {
return new DefaultAnnotationAttributeExtractor(clazz.getAnnotation(annotationType), clazz);
}
}

View File

@@ -1,149 +0,0 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.core.annotation.AnnotationUtilsTests.*;
/**
* Unit tests for {@link MapAnnotationAttributeExtractor}.
*
* @author Sam Brannen
* @since 4.2.1
*/
@SuppressWarnings("serial")
public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnnotationAttributeExtractorTestCase {
@Override
protected AnnotationAttributeExtractor<?> createExtractorFor(Class<?> clazz, String expected, Class<? extends Annotation> annotationType) {
Map<String, Object> attributes = Collections.singletonMap(expected, expected);
return new MapAnnotationAttributeExtractor(attributes, annotationType, clazz);
}
@Before
public void clearCacheBeforeTests() {
AnnotationUtils.clearCache();
}
@Test
public void enrichAndValidateAttributesWithImplicitAliasesAndMinimalAttributes() throws Exception {
Map<String, Object> attributes = new HashMap<>();
Map<String, Object> expectedAttributes = new HashMap<String, Object>() {{
put("groovyScript", "");
put("xmlFile", "");
put("value", "");
put("location1", "");
put("location2", "");
put("location3", "");
put("nonAliasedAttribute", "");
put("configClass", Object.class);
}};
assertEnrichAndValidateAttributes(attributes, expectedAttributes);
}
@Test
public void enrichAndValidateAttributesWithImplicitAliases() throws Exception {
Map<String, Object> attributes = new HashMap<String, Object>() {{
put("groovyScript", "groovy!");
}};
Map<String, Object> expectedAttributes = new HashMap<String, Object>() {{
put("groovyScript", "groovy!");
put("xmlFile", "groovy!");
put("value", "groovy!");
put("location1", "groovy!");
put("location2", "groovy!");
put("location3", "groovy!");
put("nonAliasedAttribute", "");
put("configClass", Object.class);
}};
assertEnrichAndValidateAttributes(attributes, expectedAttributes);
}
@Test
public void enrichAndValidateAttributesWithSingleElementThatOverridesAnArray() {
Map<String, Object> attributes = new HashMap<String, Object>() {{
// Intentionally storing 'value' as a single String instead of an array.
// put("value", asArray("/foo"));
put("value", "/foo");
put("name", "test");
}};
Map<String, Object> expected = new HashMap<String, Object>() {{
put("value", asArray("/foo"));
put("path", asArray("/foo"));
put("name", "test");
put("method", new RequestMethod[0]);
}};
MapAnnotationAttributeExtractor extractor = new MapAnnotationAttributeExtractor(attributes, WebMapping.class, null);
Map<String, Object> enriched = extractor.getSource();
assertEquals("attribute map size", expected.size(), enriched.size());
expected.forEach((attr, expectedValue) -> assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expectedValue)));
}
@SuppressWarnings("unchecked")
private void assertEnrichAndValidateAttributes(Map<String, Object> sourceAttributes, Map<String, Object> expected) throws Exception {
Class<? extends Annotation> annotationType = ImplicitAliasesContextConfig.class;
// Since the ordering of attribute methods returned by the JVM is non-deterministic,
// we have to rig the attributeAliasesCache in AnnotationUtils so that the tests
// consistently fail in case enrichAndValidateAttributes() is buggy.
// Otherwise, these tests would intermittently pass even for an invalid implementation.
Field cacheField = InternalAnnotationUtils.class.getDeclaredField("attributeAliasesCache");
cacheField.setAccessible(true);
Map<Class<? extends Annotation>, MultiValueMap<String, String>> attributeAliasesCache =
(Map<Class<? extends Annotation>, MultiValueMap<String, String>>) cacheField.get(null);
// Declare aliases in an order that will cause enrichAndValidateAttributes() to
// fail unless it considers all aliases in the set of implicit aliases.
MultiValueMap<String, String> aliases = new LinkedMultiValueMap<>();
aliases.put("xmlFile", Arrays.asList("value", "groovyScript", "location1", "location2", "location3"));
aliases.put("groovyScript", Arrays.asList("value", "xmlFile", "location1", "location2", "location3"));
aliases.put("value", Arrays.asList("xmlFile", "groovyScript", "location1", "location2", "location3"));
aliases.put("location1", Arrays.asList("xmlFile", "groovyScript", "value", "location2", "location3"));
aliases.put("location2", Arrays.asList("xmlFile", "groovyScript", "value", "location1", "location3"));
aliases.put("location3", Arrays.asList("xmlFile", "groovyScript", "value", "location1", "location2"));
attributeAliasesCache.put(annotationType, aliases);
MapAnnotationAttributeExtractor extractor = new MapAnnotationAttributeExtractor(sourceAttributes, annotationType, null);
Map<String, Object> enriched = extractor.getSource();
assertEquals("attribute map size", expected.size(), enriched.size());
expected.forEach((attr, expectedValue) -> assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expectedValue)));
}
}