Selective backport of annotation retrieval refinements (from 5.0.5)

In particular AnnotationTypeFilter's ignoring of standard Java interfaces, AnnotationUtils.clearCache() and a few extra common classes in ClassUtils.

Issue: SPR-16667
Issue: SPR-16675
This commit is contained in:
Juergen Hoeller
2018-03-31 17:03:59 +02:00
parent 1cbc353dd1
commit 5d54adfb9a
9 changed files with 173 additions and 177 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -417,7 +417,8 @@ class ConfigurationClassEnhancer {
@Override
public boolean isMatch(Method candidateMethod) {
return BeanAnnotationHelper.isBeanAnnotated(candidateMethod);
return (candidateMethod.getDeclaringClass() != Object.class &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}
private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {

View File

@@ -29,6 +29,7 @@ import java.util.Set;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -240,7 +241,6 @@ public class AnnotatedElementUtils {
return Boolean.TRUE.equals(
searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() {
@Override
public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
return (metaDepth > 0 ? Boolean.TRUE : CONTINUE);
@@ -270,7 +270,6 @@ public class AnnotatedElementUtils {
if (element.isAnnotationPresent(annotationType)) {
return true;
}
return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor));
}
@@ -615,7 +614,6 @@ public class AnnotatedElementUtils {
if (element.isAnnotationPresent(annotationType)) {
return true;
}
return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor));
}
@@ -873,8 +871,8 @@ public class AnnotatedElementUtils {
* @param processor the processor to delegate to
* @return the result of the processor (potentially {@code null})
*/
private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
String annotationName, Processor<T> processor) {
private static <T> T searchWithGetSemantics(AnnotatedElement element,
Class<? extends Annotation> annotationType, String annotationName, Processor<T> processor) {
return searchWithGetSemantics(element, annotationType, annotationName, null, processor);
}
@@ -893,12 +891,13 @@ public class AnnotatedElementUtils {
* @return the result of the processor (potentially {@code null})
* @since 4.3
*/
private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
String annotationName, Class<? extends Annotation> containerType, Processor<T> processor) {
private static <T> T searchWithGetSemantics(AnnotatedElement element,
Class<? extends Annotation> annotationType, String annotationName,
Class<? extends Annotation> containerType, Processor<T> processor) {
try {
return searchWithGetSemantics(element, annotationType, annotationName, containerType, processor,
new HashSet<AnnotatedElement>(), 0);
return searchWithGetSemantics(element, annotationType, annotationName,
containerType, processor, new HashSet<AnnotatedElement>(), 0);
}
catch (Throwable ex) {
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
@@ -923,8 +922,9 @@ public class AnnotatedElementUtils {
* @param metaDepth the meta-depth of the annotation
* @return the result of the processor (potentially {@code null})
*/
private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
String annotationName, Class<? extends Annotation> containerType, Processor<T> processor,
private static <T> T searchWithGetSemantics(AnnotatedElement element,
Class<? extends Annotation> annotationType, String annotationName,
Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> visited, int metaDepth) {
Assert.notNull(element, "AnnotatedElement must not be null");
@@ -939,7 +939,7 @@ public class AnnotatedElementUtils {
return result;
}
if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new
if (element instanceof Class) { // otherwise getAnnotations does not return anything new
List<Annotation> inheritedAnnotations = new ArrayList<Annotation>();
for (Annotation annotation : element.getAnnotations()) {
if (!declaredAnnotations.contains(annotation)) {
@@ -986,9 +986,9 @@ public class AnnotatedElementUtils {
* @since 4.2
*/
private static <T> T searchWithGetSemanticsInAnnotations(AnnotatedElement element,
List<Annotation> annotations, Class<? extends Annotation> annotationType, String annotationName,
Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited,
int metaDepth) {
List<Annotation> annotations, Class<? extends Annotation> annotationType,
String annotationName, Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
// Search in annotations
for (Annotation annotation : annotations) {
@@ -1054,7 +1054,8 @@ public class AnnotatedElementUtils {
* @return the result of the processor (potentially {@code null})
* @since 4.2
*/
private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
private static <T> T searchWithFindSemantics(AnnotatedElement element,
Class<? extends Annotation> annotationType,
String annotationName, Processor<T> processor) {
return searchWithFindSemantics(element, annotationType, annotationName, null, processor);
@@ -1074,8 +1075,9 @@ public class AnnotatedElementUtils {
* @return the result of the processor (potentially {@code null})
* @since 4.3
*/
private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
String annotationName, Class<? extends Annotation> containerType, Processor<T> processor) {
private static <T> T searchWithFindSemantics(AnnotatedElement element,
Class<? extends Annotation> annotationType, String annotationName,
Class<? extends Annotation> containerType, Processor<T> processor) {
if (containerType != null && !processor.aggregates()) {
throw new IllegalArgumentException(
@@ -1083,8 +1085,8 @@ public class AnnotatedElementUtils {
}
try {
return searchWithFindSemantics(
element, annotationType, annotationName, containerType, processor, new HashSet<AnnotatedElement>(), 0);
return searchWithFindSemantics(element, annotationType, annotationName,
containerType, processor, new HashSet<AnnotatedElement>(), 0);
}
catch (Throwable ex) {
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
@@ -1120,18 +1122,49 @@ public class AnnotatedElementUtils {
try {
// Locally declared annotations (ignoring @Inherited)
Annotation[] annotations = element.getDeclaredAnnotations();
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<T>() : null);
if (annotations.length > 0) {
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<T>() : null);
// Search in local annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
if (currentAnnotationType == annotationType ||
currentAnnotationType.getName().equals(annotationName) ||
processor.alwaysProcesses()) {
T result = processor.process(element, annotation, metaDepth);
// Search in local annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
if (currentAnnotationType == annotationType ||
currentAnnotationType.getName().equals(annotationName) ||
processor.alwaysProcesses()) {
T result = processor.process(element, annotation, metaDepth);
if (result != null) {
if (aggregatedResults != null && metaDepth == 0) {
aggregatedResults.add(result);
}
else {
return result;
}
}
}
// Repeatable annotations in container?
else if (currentAnnotationType == containerType) {
for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
T result = processor.process(element, contained, metaDepth);
if (aggregatedResults != null && result != null) {
// No need to post-process since repeatable annotations within a
// container cannot be composed annotations.
aggregatedResults.add(result);
}
}
}
}
}
// Recursively search in meta-annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName,
containerType, processor, visited, metaDepth + 1);
if (result != null) {
if (processor.aggregates() && metaDepth == 0) {
processor.postProcess(currentAnnotationType, annotation, result);
if (aggregatedResults != null && metaDepth == 0) {
aggregatedResults.add(result);
}
else {
@@ -1139,60 +1172,36 @@ public class AnnotatedElementUtils {
}
}
}
// Repeatable annotations in container?
else if (currentAnnotationType == containerType) {
for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
T result = processor.process(element, contained, metaDepth);
if (result != null) {
// No need to post-process since repeatable annotations within a
// container cannot be composed annotations.
aggregatedResults.add(result);
}
}
}
}
}
// Search in meta annotations on local annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName,
containerType, processor, visited, metaDepth + 1);
if (result != null) {
processor.postProcess(currentAnnotationType, annotation, result);
if (processor.aggregates() && metaDepth == 0) {
aggregatedResults.add(result);
}
else {
return result;
}
}
if (!CollectionUtils.isEmpty(aggregatedResults)) {
// Prepend to support top-down ordering within class hierarchies
processor.getAggregatedResults().addAll(0, aggregatedResults);
}
}
if (processor.aggregates()) {
// Prepend to support top-down ordering within class hierarchies
processor.getAggregatedResults().addAll(0, aggregatedResults);
}
if (element instanceof Method) {
Method method = (Method) element;
T result;
// Search on possibly bridged method
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
T result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName, containerType,
processor, visited, metaDepth);
if (result != null) {
return result;
if (resolvedMethod != method) {
result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
// Search on methods in interfaces declared locally
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
result = searchOnInterfaces(method, annotationType, annotationName, containerType, processor,
visited, metaDepth, ifcs);
if (result != null) {
return result;
if (ifcs.length > 0) {
result = searchOnInterfaces(method, annotationType, annotationName,
containerType, processor, visited, metaDepth, ifcs);
if (result != null) {
return result;
}
}
// Search on methods in class hierarchy and interface hierarchy
@@ -1202,7 +1211,6 @@ public class AnnotatedElementUtils {
if (clazz == null || Object.class == clazz) {
break;
}
try {
Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod);
@@ -1215,10 +1223,9 @@ public class AnnotatedElementUtils {
catch (NoSuchMethodException ex) {
// No equivalent method found
}
// Search on interfaces declared on superclass
result = searchOnInterfaces(method, annotationType, annotationName, containerType, processor,
visited, metaDepth, clazz.getInterfaces());
result = searchOnInterfaces(method, annotationType, annotationName,
containerType, processor, visited, metaDepth, clazz.getInterfaces());
if (result != null) {
return result;
}
@@ -1229,8 +1236,8 @@ public class AnnotatedElementUtils {
// Search on interfaces
for (Class<?> ifc : clazz.getInterfaces()) {
T result = searchWithFindSemantics(ifc, annotationType, annotationName, containerType,
processor, visited, metaDepth);
T result = searchWithFindSemantics(ifc, annotationType, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
@@ -1239,8 +1246,8 @@ public class AnnotatedElementUtils {
// Search on superclass
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && Object.class != superclass) {
T result = searchWithFindSemantics(superclass, annotationType, annotationName, containerType,
processor, visited, metaDepth);
T result = searchWithFindSemantics(superclass, annotationType, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}

View File

@@ -145,17 +145,17 @@ public abstract class AnnotationUtils {
* <p>Note that this method supports only a single level of meta-annotations.
* For support for arbitrary levels of meta-annotations, use one of the
* {@code find*()} methods instead.
* @param ann the Annotation to check
* @param annotation the Annotation to check
* @param annotationType the annotation type to look for, both locally and as a meta-annotation
* @return the first matching annotation, or {@code null} if not found
* @since 4.0
*/
@SuppressWarnings("unchecked")
public static <A extends Annotation> A getAnnotation(Annotation ann, Class<A> annotationType) {
if (annotationType.isInstance(ann)) {
return synthesizeAnnotation((A) ann);
public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) {
if (annotationType.isInstance(annotation)) {
return synthesizeAnnotation((A) annotation);
}
Class<? extends Annotation> annotatedElement = ann.annotationType();
Class<? extends Annotation> annotatedElement = annotation.annotationType();
try {
return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement);
}
@@ -568,7 +568,6 @@ public abstract class AnnotationUtils {
if (result == null) {
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);
if (result == null) {
result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());
}
@@ -603,10 +602,10 @@ public abstract class AnnotationUtils {
private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
A annotation = null;
for (Class<?> iface : ifcs) {
if (isInterfaceWithAnnotatedMethods(iface)) {
for (Class<?> ifc : ifcs) {
if (isInterfaceWithAnnotatedMethods(ifc)) {
try {
Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());
Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes());
annotation = getAnnotation(equivalentMethod, annotationType);
}
catch (NoSuchMethodException ex) {
@@ -620,13 +619,13 @@ public abstract class AnnotationUtils {
return annotation;
}
static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
Boolean found = annotatedInterfaceCache.get(iface);
static boolean isInterfaceWithAnnotatedMethods(Class<?> ifc) {
Boolean found = annotatedInterfaceCache.get(ifc);
if (found != null) {
return found;
}
found = Boolean.FALSE;
for (Method ifcMethod : iface.getMethods()) {
for (Method ifcMethod : ifc.getMethods()) {
try {
if (ifcMethod.getAnnotations().length > 0) {
found = Boolean.TRUE;
@@ -637,7 +636,7 @@ public abstract class AnnotationUtils {
handleIntrospectionFailure(ifcMethod, ex);
}
}
annotatedInterfaceCache.put(iface, found);
annotatedInterfaceCache.put(ifc, found);
return found;
}
@@ -1286,15 +1285,12 @@ public abstract class AnnotationUtils {
}
Object value = attributes.get(attributeName);
boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder));
for (String aliasedAttributeName : aliasMap.get(attributeName)) {
if (valuesAlreadyReplaced.contains(aliasedAttributeName)) {
continue;
}
Object aliasedValue = attributes.get(aliasedAttributeName);
boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder));
// Something to validate or replace with an alias?
if (valuePresent || aliasPresent) {
if (valuePresent && aliasPresent) {
@@ -1919,6 +1915,20 @@ public abstract class AnnotationUtils {
}
}
/**
* Clear the internal annotation metadata cache.
* @since 4.3.15
*/
public static void clearCache() {
findAnnotationCache.clear();
metaPresentCache.clear();
annotatedInterfaceCache.clear();
synthesizableCache.clear();
attributeAliasesCache.clear();
attributeMethodsCache.clear();
aliasDescriptorCache.clear();
}
/**
* Cache key for the AnnotatedElement cache.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2018 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.
@@ -70,7 +70,9 @@ public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter
* @param considerMetaAnnotations whether to also match on meta-annotations
* @param considerInterfaces whether to also match interfaces
*/
public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) {
public AnnotationTypeFilter(
Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) {
super(annotationType.isAnnotationPresent(Inherited.class), considerInterfaces);
this.annotationType = annotationType;
this.considerMetaAnnotations = considerMetaAnnotations;
@@ -99,6 +101,11 @@ public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter
return false;
}
else if (typeName.startsWith("java")) {
if (!this.annotationType.getName().startsWith("java")) {
// Standard Java types do not have non-standard annotations on them ->
// skip any load attempt, in particular for Java language interfaces.
return false;
}
try {
Class<?> clazz = ClassUtils.forName(typeName, getClass().getClassLoader());
return ((this.considerMetaAnnotations ? AnnotationUtils.getAnnotation(clazz, this.annotationType) :

View File

@@ -112,7 +112,7 @@ public abstract class ClassUtils {
registerCommonClasses(entry.getKey());
}
Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(32);
Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(64);
primitiveTypes.addAll(primitiveWrapperTypeMap.values());
primitiveTypes.addAll(Arrays.asList(new Class<?>[] {
boolean[].class, byte[].class, char[].class, double[].class,
@@ -125,9 +125,10 @@ public abstract class ClassUtils {
registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class,
Float[].class, Integer[].class, Long[].class, Short[].class);
registerCommonClasses(Number.class, Number[].class, String.class, String[].class,
Object.class, Object[].class, Class.class, Class[].class);
Class.class, Class[].class, Object.class, Object[].class);
registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class,
Error.class, StackTraceElement.class, StackTraceElement[].class);
registerCommonClasses(Enum.class, Iterable.class, Cloneable.class, Comparable.class);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -22,7 +22,6 @@ import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
@@ -38,7 +37,6 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;
@@ -63,23 +61,8 @@ public class AnnotationUtilsTests {
@Before
public void clearCachesBeforeTests() {
clearCaches();
}
static void clearCaches() {
clearCache("findAnnotationCache", "annotatedInterfaceCache", "metaPresentCache", "synthesizableCache",
"attributeAliasesCache", "attributeMethodsCache", "aliasDescriptorCache");
}
static void clearCache(String... cacheNames) {
stream(cacheNames).forEach(cacheName -> getCache(cacheName).clear());
}
static Map<?, ?> getCache(String cacheName) {
Field field = ReflectionUtils.findField(AnnotationUtils.class, cacheName);
ReflectionUtils.makeAccessible(field);
return (Map<?, ?>) ReflectionUtils.getField(field, null);
public void clearCacheBeforeTests() {
AnnotationUtils.clearCache();
}
@@ -727,7 +710,7 @@ public class AnnotationUtilsTests {
public void getAttributeOverrideNameFromWrongTargetAnnotation() throws Exception {
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
assertThat("xmlConfigFile is not an alias for @Component.",
getAttributeOverrideName(attribute, Component.class), is(nullValue()));
getAttributeOverrideName(attribute, Component.class), is(nullValue()));
}
@Test
@@ -860,12 +843,6 @@ public class AnnotationUtilsTests {
assertEquals("value attribute: ", "webController", synthesizedComponent.value());
}
@Test
public void synthesizeAnnotationsFromNullSources() throws Exception {
assertNull("null annotation", synthesizeAnnotation(null, null));
assertNull("null map", synthesizeAnnotation(null, WebMapping.class, null));
}
@Test
public void synthesizeAlreadySynthesizedAnnotation() throws Exception {
Method method = WebController.class.getMethod("handleMappedWithValueAttribute");
@@ -927,7 +904,6 @@ public class AnnotationUtilsTests {
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()));
@@ -1035,18 +1011,18 @@ public class AnnotationUtilsTests {
@Test
public void synthesizeAnnotationWithImplicitAliasesWithImpliedAliasNamesOmitted() throws Exception {
assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(
ValueImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "value");
ValueImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "value");
assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(
LocationsImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "location");
LocationsImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "location");
assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(
XmlFilesImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "xmlFile");
XmlFilesImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "xmlFile");
}
private void assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(Class<?> clazz,
String expected) throws Exception {
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig config = clazz.getAnnotation(
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class);
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class);
assertNotNull(config);
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig synthesizedConfig = synthesizeAnnotation(config);
@@ -1241,9 +1217,11 @@ public class AnnotationUtilsTests {
assertEquals("location: ", "", contextConfig.location());
}
@Test(expected = AnnotationConfigurationException.class)
public void synthesizeAnnotationWithAttributeAliasesDifferentValues() throws Exception {
getValue(synthesizeAnnotation(ContextConfigMismatch.class.getAnnotation(ContextConfig.class)));
@Test
public void synthesizeAnnotationWithAttributeAliasesWithDifferentValues() throws Exception {
ContextConfig contextConfig = synthesizeAnnotation(ContextConfigMismatch.class.getAnnotation(ContextConfig.class));
exception.expect(AnnotationConfigurationException.class);
getValue(contextConfig);
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -17,6 +17,7 @@
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;
@@ -25,9 +26,6 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.annotation.AnnotationUtilsTests.ImplicitAliasesContextConfig;
import org.springframework.core.annotation.AnnotationUtilsTests.RequestMethod;
import org.springframework.core.annotation.AnnotationUtilsTests.WebMapping;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -44,14 +42,21 @@ import static org.springframework.core.annotation.AnnotationUtilsTests.*;
@SuppressWarnings("serial")
public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnnotationAttributeExtractorTestCase {
@Before
public void clearCachesBeforeTests() {
AnnotationUtilsTests.clearCaches();
@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() {
Map<String, Object> attributes = new HashMap<String, Object>();
public void enrichAndValidateAttributesWithImplicitAliasesAndMinimalAttributes() throws Exception {
Map<String, Object> attributes = new HashMap<>();
Map<String, Object> expectedAttributes = new HashMap<String, Object>() {{
put("groovyScript", "");
put("xmlFile", "");
@@ -67,7 +72,7 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
}
@Test
public void enrichAndValidateAttributesWithImplicitAliases() {
public void enrichAndValidateAttributesWithImplicitAliases() throws Exception {
Map<String, Object> attributes = new HashMap<String, Object>() {{
put("groovyScript", "groovy!");
}};
@@ -88,7 +93,6 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
@Test
public void enrichAndValidateAttributesWithSingleElementThatOverridesAnArray() {
// @formatter:off
Map<String, Object> attributes = new HashMap<String, Object>() {{
// Intentionally storing 'value' as a single String instead of an array.
// put("value", asArray("/foo"));
@@ -102,7 +106,6 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
put("name", "test");
put("method", new RequestMethod[0]);
}};
// @formatter:on
MapAnnotationAttributeExtractor extractor = new MapAnnotationAttributeExtractor(attributes, WebMapping.class, null);
Map<String, Object> enriched = extractor.getSource();
@@ -112,22 +115,21 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
}
@SuppressWarnings("unchecked")
private void assertEnrichAndValidateAttributes(Map<String, Object> sourceAttributes, Map<String, Object> expected) {
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.
// 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 = AnnotationUtils.class.getDeclaredField("attributeAliasesCache");
cacheField.setAccessible(true);
Map<Class<? extends Annotation>, MultiValueMap<String, String>> attributeAliasesCache =
(Map<Class<? extends Annotation>, MultiValueMap<String, String>>) AnnotationUtilsTests.getCache("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<String, String>();
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"));
@@ -144,10 +146,4 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
expected.forEach((attr, expectedValue) -> assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expectedValue)));
}
@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);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2018 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.
@@ -49,7 +49,7 @@ public class AnnotationTypeFilterTests {
@Test
public void testInheritedAnnotationFromInterfaceDoesNotMatch() throws Exception {
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
String classUnderTest = "org.springframework.core.type.AnnotationTypeFilterTests$SomeSubClassOfSomeComponentInterface";
String classUnderTest = "org.springframework.core.type.AnnotationTypeFilterTests$SomeClassWithSomeComponentInterface";
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(classUnderTest);
AnnotationTypeFilter filter = new AnnotationTypeFilter(InheritedAnnotation.class);
@@ -61,7 +61,7 @@ public class AnnotationTypeFilterTests {
@Test
public void testInheritedAnnotationFromBaseClassDoesMatch() throws Exception {
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
String classUnderTest = "org.springframework.core.type.AnnotationTypeFilterTests$SomeSubClassOfSomeComponent";
String classUnderTest = "org.springframework.core.type.AnnotationTypeFilterTests$SomeSubclassOfSomeComponent";
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(classUnderTest);
AnnotationTypeFilter filter = new AnnotationTypeFilter(InheritedAnnotation.class);
@@ -94,22 +94,21 @@ public class AnnotationTypeFilterTests {
@Test
public void testMatchesInterfacesIfConfigured() throws Exception {
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
String classUnderTest = "org.springframework.core.type.AnnotationTypeFilterTests$SomeComponentInterface";
String classUnderTest = "org.springframework.core.type.AnnotationTypeFilterTests$SomeClassWithSomeComponentInterface";
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(classUnderTest);
AnnotationTypeFilter filter = new AnnotationTypeFilter(InheritedAnnotation.class, false, true);
assertTrue(filter.match(metadataReader, metadataReaderFactory));
ClassloadingAssertions.assertClassNotLoaded(classUnderTest);
}
// We must use a standalone set of types to ensure that no one else is loading them
// and interfering with ClassloadingAssertions.assertClassNotLoaded()
@Inherited
private static @interface InheritedAnnotation {
private @interface InheritedAnnotation {
}
@@ -119,21 +118,21 @@ public class AnnotationTypeFilterTests {
@InheritedAnnotation
private static interface SomeComponentInterface {
private interface SomeComponentInterface {
}
@SuppressWarnings("unused")
private static class SomeSubClassOfSomeComponentInterface implements SomeComponentInterface {
private static class SomeClassWithSomeComponentInterface implements Cloneable, SomeComponentInterface {
}
@SuppressWarnings("unused")
private static class SomeSubClassOfSomeComponent extends SomeComponent {
private static class SomeSubclassOfSomeComponent extends SomeComponent {
}
private static @interface NonInheritedAnnotation {
private @interface NonInheritedAnnotation {
}

View File

@@ -80,7 +80,6 @@ public final class MockMvc {
* @see org.springframework.test.web.servlet.setup.MockMvcBuilders
*/
MockMvc(TestDispatcherServlet servlet, Filter[] filters, ServletContext servletContext) {
Assert.notNull(servlet, "DispatcherServlet is required");
Assert.notNull(filters, "filters cannot be null");
Assert.noNullElements(filters, "filters cannot contain null values");
@@ -131,7 +130,6 @@ public final class MockMvc {
* @see org.springframework.test.web.servlet.result.MockMvcResultMatchers
*/
public ResultActions perform(RequestBuilder requestBuilder) throws Exception {
if (this.defaultRequestBuilder != null) {
if (requestBuilder instanceof Mergeable) {
requestBuilder = (RequestBuilder) ((Mergeable) requestBuilder).merge(this.defaultRequestBuilder);
@@ -190,7 +188,6 @@ public final class MockMvc {
for (ResultMatcher matcher : this.defaultResultMatchers) {
matcher.match(mvcResult);
}
for (ResultHandler handler : this.defaultResultHandlers) {
handler.handle(mvcResult);
}