Migrate and verify annotation utility methods

Migrate all possible `AnnotationUtils` and `AnnotatedElementUtils`
method to the `MergedAnnotation` API, verify results against the old
implementations.

All migrated methods now call both the new API and the old version and
ensure that the same results or exceptions are raised. A full build of
both Spring Framework and Spring Boot has been executed to ensure, as
much as possible, that the migration does not cause unexpected
regressions.

See gh-22562
This commit is contained in:
Phillip Webb
2019-01-23 15:21:09 -08:00
committed by Juergen Hoeller
parent b91ccf038f
commit a14bfe9a21
9 changed files with 1136 additions and 192 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* 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.
@@ -459,7 +459,9 @@ public class AnnotatedElementUtilsTests {
exception.expectMessage(either(
containsString("values of [{duplicateDeclaration}] and [{requiredLocationsDeclaration}]")).or(
containsString("values of [{requiredLocationsDeclaration}] and [{duplicateDeclaration}]")));
exception.expectMessage(containsString("but only one is permitted"));
exception.expectMessage(either(
containsString("but only one is permitted")).or(
containsString("Different @AliasFor mirror values for annotation")));
getMergedAnnotationAttributes(element, ContextConfig.class);
}

View File

@@ -562,7 +562,9 @@ public class AnnotationUtilsTests {
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("Attribute 'value' in"));
exception.expectMessage(containsString(BrokenContextConfig.class.getName()));
exception.expectMessage(containsString("@AliasFor [location]"));
exception.expectMessage(either(
containsString("@AliasFor [location]")).or(
containsString("@AliasFor 'location'")));
getRepeatableAnnotations(BrokenConfigHierarchyTestCase.class, BrokenContextConfig.class, BrokenHierarchy.class);
}
@@ -897,7 +899,9 @@ public class AnnotationUtilsTests {
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("In @AliasFor declared on attribute 'foo' in annotation"));
exception.expectMessage(containsString(AliasForWithDuplicateAttributeDeclaration.class.getName()));
exception.expectMessage(containsString("attribute 'attribute' and its alias 'value' are present with values of [baz] and [bar]"));
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);
}
@@ -905,9 +909,14 @@ public class AnnotationUtilsTests {
public void synthesizeAnnotationWithAttributeAliasForNonexistentAttribute() throws Exception {
AliasForNonexistentAttribute annotation = AliasForNonexistentAttributeClass.class.getAnnotation(AliasForNonexistentAttribute.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("Attribute 'foo' in"));
exception.expectMessage(containsString(AliasForNonexistentAttribute.class.getName()));
exception.expectMessage(containsString("is declared as an @AliasFor nonexistent attribute 'bar'"));
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);
}
@@ -916,9 +925,9 @@ public class AnnotationUtilsTests {
AliasForWithoutMirroredAliasFor annotation =
AliasForWithoutMirroredAliasForClass.class.getAnnotation(AliasForWithoutMirroredAliasFor.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("Attribute 'bar' in"));
exception.expectMessage(containsString(AliasForWithoutMirroredAliasFor.class.getName()));
exception.expectMessage(containsString("@AliasFor [foo]"));
exception.expectMessage(allOf(startsWith("Attribute 'bar' in"),
containsString(AliasForWithoutMirroredAliasFor.class.getName()),
either(containsString("@AliasFor [foo]")).or(containsString("@AliasFor 'foo'"))));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
}
@@ -929,8 +938,10 @@ public class AnnotationUtilsTests {
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("Attribute 'bar' in"));
exception.expectMessage(containsString(AliasForWithMirroredAliasForWrongAttribute.class.getName()));
exception.expectMessage(either(containsString("must be declared as an @AliasFor [foo], not [quux]")).
or(containsString("is declared as an @AliasFor nonexistent attribute 'quux'")));
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);
}
@@ -980,7 +991,7 @@ public class AnnotationUtilsTests {
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' in meta-annotation"));
exception.expectMessage(containsString("declares an alias for attribute 'location'"));
exception.expectMessage(containsString(ContextConfig.class.getName()));
exception.expectMessage(containsString("not meta-present"));
InternalAnnotationUtils.synthesizeAnnotation(annotation);
@@ -1133,21 +1144,16 @@ public class AnnotationUtilsTests {
ImplicitAliasesWithDuplicateValuesContextConfig config = clazz.getAnnotation(annotationType);
assertNotNull(config);
ImplicitAliasesWithDuplicateValuesContextConfig synthesizedConfig = synthesizeAnnotation(config, clazz);
assertNotNull(synthesizedConfig);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(startsWith("In annotation"));
exception.expectMessage(either(startsWith("In annotation")).or(startsWith("Different @AliasFor mirror values")));
exception.expectMessage(containsString(annotationType.getName()));
exception.expectMessage(containsString("declared on class"));
exception.expectMessage(containsString(clazz.getName()));
exception.expectMessage(containsString("and synthesized from"));
exception.expectMessage(either(containsString("attribute 'location1' and its alias 'location2'")).or(
containsString("attribute 'location2' and its alias 'location1'")));
exception.expectMessage(either(containsString("are present with values of [1] and [2]")).or(
containsString("are present with values of [2] and [1]")));
synthesizedConfig.location1();
exception.expectMessage(either(containsString("with values of [1] and [2]")).or(
containsString("with values of [2] and [1]")));
synthesizeAnnotation(config, clazz).location1();
}
@Test
@@ -1241,8 +1247,8 @@ public class AnnotationUtilsTests {
@Test
public void synthesizeAnnotationWithAttributeAliasesWithDifferentValues() throws Exception {
ContextConfig contextConfig = InternalAnnotationUtils.synthesizeAnnotation(ContextConfigMismatch.class.getAnnotation(ContextConfig.class));
exception.expect(AnnotationConfigurationException.class);
ContextConfig contextConfig = InternalAnnotationUtils.synthesizeAnnotation(ContextConfigMismatch.class.getAnnotation(ContextConfig.class));
getValue(contextConfig);
}
@@ -1306,23 +1312,29 @@ public class AnnotationUtilsTests {
private void assertMissingTextAttribute(Map<String, Object> attributes) {
exception.expect(IllegalArgumentException.class);
exception.expectMessage(startsWith("Attributes map"));
exception.expectMessage(containsString("returned null for required attribute 'text'"));
exception.expectMessage(containsString("defined by annotation type [" + AnnotationWithoutDefaults.class.getName() + "]"));
exception.expectMessage(either(allOf(startsWith("Attributes map"),
containsString("returned null for required attribute 'text'"),
containsString("defined by annotation type ["
+ AnnotationWithoutDefaults.class.getName() + "]"))).or(
containsString(
"No value found for attribute named 'text' in merged annotation")));
synthesizeAnnotation(attributes, AnnotationWithoutDefaults.class, null);
}
@Test
public void synthesizeAnnotationFromMapWithAttributeOfIncorrectType() throws Exception {
Map<String, Object> map = Collections.singletonMap(VALUE, 42L);
exception.expect(IllegalArgumentException.class);
exception.expectMessage(startsWith("Attributes map"));
exception.expectMessage(containsString("returned a value of type [java.lang.Long]"));
exception.expectMessage(containsString("for attribute 'value'"));
exception.expectMessage(containsString("but a value of type [java.lang.String] is required"));
exception.expectMessage(containsString("as defined by annotation type [" + Component.class.getName() + "]"));
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")));
synthesizeAnnotation(map, Component.class, null);
}