diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 294d765c1c..e26b1e23ff 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -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) { diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java index 01fec3f0d4..75f42f02de 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java @@ -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() { - @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 searchWithGetSemantics(AnnotatedElement element, Class annotationType, - String annotationName, Processor processor) { + private static T searchWithGetSemantics(AnnotatedElement element, + Class annotationType, String annotationName, Processor 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 searchWithGetSemantics(AnnotatedElement element, Class annotationType, - String annotationName, Class containerType, Processor processor) { + private static T searchWithGetSemantics(AnnotatedElement element, + Class annotationType, String annotationName, + Class containerType, Processor processor) { try { - return searchWithGetSemantics(element, annotationType, annotationName, containerType, processor, - new HashSet(), 0); + return searchWithGetSemantics(element, annotationType, annotationName, + containerType, processor, new HashSet(), 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 searchWithGetSemantics(AnnotatedElement element, Class annotationType, - String annotationName, Class containerType, Processor processor, + private static T searchWithGetSemantics(AnnotatedElement element, + Class annotationType, String annotationName, + Class containerType, Processor processor, Set 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 inheritedAnnotations = new ArrayList(); for (Annotation annotation : element.getAnnotations()) { if (!declaredAnnotations.contains(annotation)) { @@ -986,9 +986,9 @@ public class AnnotatedElementUtils { * @since 4.2 */ private static T searchWithGetSemanticsInAnnotations(AnnotatedElement element, - List annotations, Class annotationType, String annotationName, - Class containerType, Processor processor, Set visited, - int metaDepth) { + List annotations, Class annotationType, + String annotationName, Class containerType, + Processor processor, Set 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 searchWithFindSemantics(AnnotatedElement element, Class annotationType, + private static T searchWithFindSemantics(AnnotatedElement element, + Class annotationType, String annotationName, Processor 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 searchWithFindSemantics(AnnotatedElement element, Class annotationType, - String annotationName, Class containerType, Processor processor) { + private static T searchWithFindSemantics(AnnotatedElement element, + Class annotationType, String annotationName, + Class containerType, Processor 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(), 0); + return searchWithFindSemantics(element, annotationType, annotationName, + containerType, processor, new HashSet(), 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 aggregatedResults = (processor.aggregates() ? new ArrayList() : null); + if (annotations.length > 0) { + List aggregatedResults = (processor.aggregates() ? new ArrayList() : null); - // Search in local annotations - for (Annotation annotation : annotations) { - Class 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 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 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 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; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index ed091d85d6..971568dab0 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -145,17 +145,17 @@ public abstract class AnnotationUtils { *

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 getAnnotation(Annotation ann, Class annotationType) { - if (annotationType.isInstance(ann)) { - return synthesizeAnnotation((A) ann); + public static A getAnnotation(Annotation annotation, Class annotationType) { + if (annotationType.isInstance(annotation)) { + return synthesizeAnnotation((A) annotation); } - Class annotatedElement = ann.annotationType(); + Class 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 searchOnInterfaces(Method method, Class 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. diff --git a/spring-core/src/main/java/org/springframework/core/type/filter/AnnotationTypeFilter.java b/spring-core/src/main/java/org/springframework/core/type/filter/AnnotationTypeFilter.java index 2b2faa4d05..9aaea29032 100644 --- a/spring-core/src/main/java/org/springframework/core/type/filter/AnnotationTypeFilter.java +++ b/spring-core/src/main/java/org/springframework/core/type/filter/AnnotationTypeFilter.java @@ -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 annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) { + public AnnotationTypeFilter( + Class 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) : diff --git a/spring-core/src/main/java/org/springframework/util/ClassUtils.java b/spring-core/src/main/java/org/springframework/util/ClassUtils.java index 07481ed13b..89418ced65 100644 --- a/spring-core/src/main/java/org/springframework/util/ClassUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ClassUtils.java @@ -112,7 +112,7 @@ public abstract class ClassUtils { registerCommonClasses(entry.getKey()); } - Set> primitiveTypes = new HashSet>(32); + Set> primitiveTypes = new HashSet>(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); } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java index ca033bb1ba..4cbd81975c 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java @@ -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 diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MapAnnotationAttributeExtractorTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MapAnnotationAttributeExtractorTests.java index d3b456023b..3fa9039637 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MapAnnotationAttributeExtractorTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MapAnnotationAttributeExtractorTests.java @@ -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 annotationType) { + Map attributes = Collections.singletonMap(expected, expected); + return new MapAnnotationAttributeExtractor(attributes, annotationType, clazz); } + @Before + public void clearCacheBeforeTests() { + AnnotationUtils.clearCache(); + } + + @Test - public void enrichAndValidateAttributesWithImplicitAliasesAndMinimalAttributes() { - Map attributes = new HashMap(); + public void enrichAndValidateAttributesWithImplicitAliasesAndMinimalAttributes() throws Exception { + Map attributes = new HashMap<>(); Map expectedAttributes = new HashMap() {{ put("groovyScript", ""); put("xmlFile", ""); @@ -67,7 +72,7 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno } @Test - public void enrichAndValidateAttributesWithImplicitAliases() { + public void enrichAndValidateAttributesWithImplicitAliases() throws Exception { Map attributes = new HashMap() {{ put("groovyScript", "groovy!"); }}; @@ -88,7 +93,6 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno @Test public void enrichAndValidateAttributesWithSingleElementThatOverridesAnArray() { - // @formatter:off Map attributes = new HashMap() {{ // 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 enriched = extractor.getSource(); @@ -112,22 +115,21 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno } @SuppressWarnings("unchecked") - private void assertEnrichAndValidateAttributes(Map sourceAttributes, Map expected) { + private void assertEnrichAndValidateAttributes(Map sourceAttributes, Map expected) throws Exception { Class 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, MultiValueMap> attributeAliasesCache = - (Map, MultiValueMap>) AnnotationUtilsTests.getCache("attributeAliasesCache"); + (Map, MultiValueMap>) 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 aliases = new LinkedMultiValueMap(); + MultiValueMap 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 annotationType) { - Map attributes = Collections.singletonMap(expected, expected); - return new MapAnnotationAttributeExtractor(attributes, annotationType, clazz); - } - } diff --git a/spring-core/src/test/java/org/springframework/core/type/AnnotationTypeFilterTests.java b/spring-core/src/test/java/org/springframework/core/type/AnnotationTypeFilterTests.java index 6dd6dace0b..01a679d646 100644 --- a/spring-core/src/test/java/org/springframework/core/type/AnnotationTypeFilterTests.java +++ b/spring-core/src/test/java/org/springframework/core/type/AnnotationTypeFilterTests.java @@ -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 { } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java index 70a605c051..1734dad294 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java @@ -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); }