diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 12c2bb35e8..c43ce982d2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -120,7 +120,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean protected final Log logger = LogFactory.getLog(getClass()); - private final Set> autowiredAnnotationTypes = new LinkedHashSet<>(); + private final Set> autowiredAnnotationTypes = new LinkedHashSet<>(4); private String requiredParameterName = "required"; @@ -346,8 +346,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) { candidateConstructors = new Constructor[] {rawCandidates[0]}; } - else if (nonSyntheticConstructors == 2 && primaryConstructor != null - && defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) { + else if (nonSyntheticConstructors == 2 && primaryConstructor != null && + defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) { candidateConstructors = new Constructor[] {primaryConstructor, defaultConstructor}; } else if (nonSyntheticConstructors == 1 && primaryConstructor != null) { @@ -478,7 +478,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean @Nullable private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) { - if (ao.getAnnotations().length > 0) { + if (ao.getAnnotations().length > 0) { // autowiring annotations have to be local for (Class type : this.autowiredAnnotationTypes) { AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type); if (attributes != null) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index 19a6424231..45b6b8ef37 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.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. @@ -347,10 +347,12 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa */ @Nullable protected Object findValue(Annotation[] annotationsToSearch) { - AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( - AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); - if (attr != null) { - return extractValue(attr); + if (annotationsToSearch.length > 0) { // qualifier annotations have to be local + AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( + AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); + if (attr != null) { + return extractValue(attr); + } } return null; } diff --git a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java index 52bb3d2e3b..956af11544 100644 --- a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java +++ b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.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. @@ -52,13 +52,13 @@ public class AnnotationCacheOperationSourceTests { @Test - public void singularAnnotation() throws Exception { + public void singularAnnotation() { Collection ops = getOps(AnnotatedClass.class, "singular", 1); assertTrue(ops.iterator().next() instanceof CacheableOperation); } @Test - public void multipleAnnotation() throws Exception { + public void multipleAnnotation() { Collection ops = getOps(AnnotatedClass.class, "multiple", 2); Iterator it = ops.iterator(); assertTrue(it.next() instanceof CacheableOperation); @@ -66,7 +66,7 @@ public class AnnotationCacheOperationSourceTests { } @Test - public void caching() throws Exception { + public void caching() { Collection ops = getOps(AnnotatedClass.class, "caching", 2); Iterator it = ops.iterator(); assertTrue(it.next() instanceof CacheableOperation); @@ -74,18 +74,18 @@ public class AnnotationCacheOperationSourceTests { } @Test - public void emptyCaching() throws Exception { + public void emptyCaching() { getOps(AnnotatedClass.class, "emptyCaching", 0); } @Test - public void singularStereotype() throws Exception { + public void singularStereotype() { Collection ops = getOps(AnnotatedClass.class, "singleStereotype", 1); assertTrue(ops.iterator().next() instanceof CacheEvictOperation); } @Test - public void multipleStereotypes() throws Exception { + public void multipleStereotypes() { Collection ops = getOps(AnnotatedClass.class, "multipleStereotype", 3); Iterator it = ops.iterator(); assertTrue(it.next() instanceof CacheableOperation); @@ -98,7 +98,7 @@ public class AnnotationCacheOperationSourceTests { } @Test - public void singleComposedAnnotation() throws Exception { + public void singleComposedAnnotation() { Collection ops = getOps(AnnotatedClass.class, "singleComposed", 2); Iterator it = ops.iterator(); @@ -114,7 +114,7 @@ public class AnnotationCacheOperationSourceTests { } @Test - public void multipleComposedAnnotations() throws Exception { + public void multipleComposedAnnotations() { Collection ops = getOps(AnnotatedClass.class, "multipleComposed", 4); Iterator it = ops.iterator(); 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 6b29f50e4d..34563e2c6c 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 @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -854,19 +855,21 @@ public class AnnotatedElementUtils { return result; } - if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new - List inheritedAnnotations = new ArrayList<>(); - for (Annotation annotation : element.getAnnotations()) { - if (!declaredAnnotations.contains(annotation)) { - inheritedAnnotations.add(annotation); + if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new + Class superclass = ((Class) element).getSuperclass(); + if (superclass != null && superclass != Object.class) { + List inheritedAnnotations = new LinkedList<>(); + for (Annotation annotation : element.getAnnotations()) { + if (!declaredAnnotations.contains(annotation)) { + inheritedAnnotations.add(annotation); + } + } + // Continue searching within inherited annotations + result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, + annotationType, annotationName, containerType, processor, visited, metaDepth); + if (result != null) { + return result; } - } - - // Continue searching within inherited annotations - result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, - annotationType, annotationName, containerType, processor, visited, metaDepth); - if (result != null) { - return result; } } } @@ -1126,7 +1129,7 @@ public class AnnotatedElementUtils { Class clazz = method.getDeclaringClass(); while (true) { clazz = clazz.getSuperclass(); - if (clazz == null || Object.class == clazz) { + if (clazz == null || clazz == Object.class) { break; } Set annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz); @@ -1152,23 +1155,23 @@ public class AnnotatedElementUtils { } else if (element instanceof Class) { Class clazz = (Class) element; - - // Search on interfaces - for (Class ifc : clazz.getInterfaces()) { - T result = searchWithFindSemantics(ifc, annotationType, annotationName, - containerType, processor, visited, metaDepth); - if (result != null) { - return result; + if (!Annotation.class.isAssignableFrom(clazz)) { + // Search on interfaces + for (Class ifc : clazz.getInterfaces()) { + T result = searchWithFindSemantics(ifc, annotationType, annotationName, + containerType, processor, visited, metaDepth); + if (result != null) { + return result; + } } - } - - // Search on superclass - Class superclass = clazz.getSuperclass(); - if (superclass != null && Object.class != superclass) { - T result = searchWithFindSemantics(superclass, annotationType, annotationName, - containerType, processor, visited, metaDepth); - if (result != null) { - return result; + // Search on superclass + Class superclass = clazz.getSuperclass(); + if (superclass != null && superclass != Object.class) { + 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 4698698e53..9402c712a7 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 @@ -346,7 +346,7 @@ public abstract class AnnotationUtils { if (annotatedElement instanceof Class) { Class superclass = ((Class) annotatedElement).getSuperclass(); - if (superclass != null && Object.class != superclass) { + if (superclass != null && superclass != Object.class) { return getRepeatableAnnotations(superclass, annotationType, containerAnnotationType); } } @@ -553,7 +553,7 @@ public abstract class AnnotationUtils { Class clazz = method.getDeclaringClass(); while (result == null) { clazz = clazz.getSuperclass(); - if (clazz == null || Object.class == clazz) { + if (clazz == null || clazz == Object.class) { break; } Set annotatedMethods = getAnnotatedMethodsInBaseType(clazz); @@ -600,6 +600,35 @@ public abstract class AnnotationUtils { return null; } + /** + * Does the given method override the given candidate method? + * @param method the overriding method + * @param candidate the potentially overridden method + * @since 5.0.8 + */ + static boolean isOverride(Method method, Method candidate) { + if (!candidate.getName().equals(method.getName()) || + candidate.getParameterCount() != method.getParameterCount()) { + return false; + } + Class[] paramTypes = method.getParameterTypes(); + if (Arrays.equals(candidate.getParameterTypes(), paramTypes)) { + return true; + } + for (int i = 0; i < paramTypes.length; i++) { + if (paramTypes[i] != ResolvableType.forMethodParameter(candidate, i, method.getDeclaringClass()).resolve()) { + return false; + } + } + return true; + } + + /** + * Determine the methods on the given type with searchable annotations on them. + * @param baseType the superclass or interface to search + * @return the cached set of annotated methods + * @since 5.0.5 + */ static Set getAnnotatedMethodsInBaseType(Class baseType) { boolean ifcCheck = baseType.isInterface(); if (ifcCheck && ClassUtils.isJavaLanguageInterface(baseType)) { @@ -634,6 +663,13 @@ public abstract class AnnotationUtils { return annotatedMethods; } + /** + * Determine whether the specified method has searchable annotations, + * i.e. not just {@code java.lang} or {@code org.springframework.lang} + * annotations such as {@link Deprecated} and {@link Nullable}. + * @param ifcMethod the interface method to check + * @@since 5.0.5 + */ private static boolean hasSearchableAnnotations(Method ifcMethod) { Annotation[] anns = ifcMethod.getAnnotations(); if (anns.length == 0) { @@ -648,23 +684,6 @@ public abstract class AnnotationUtils { return false; } - static boolean isOverride(Method method, Method candidate) { - if (!candidate.getName().equals(method.getName()) || - candidate.getParameterCount() != method.getParameterCount()) { - return false; - } - Class[] paramTypes = method.getParameterTypes(); - if (Arrays.equals(candidate.getParameterTypes(), paramTypes)) { - return true; - } - for (int i = 0; i < paramTypes.length; i++) { - if (paramTypes[i] != ResolvableType.forMethodParameter(candidate, i, method.getDeclaringClass()).resolve()) { - return false; - } - } - return true; - } - /** * Find a single {@link Annotation} of {@code annotationType} on the * supplied {@link Class}, traversing its interfaces, annotations, and @@ -763,7 +782,7 @@ public abstract class AnnotationUtils { } Class superclass = clazz.getSuperclass(); - if (superclass == null || Object.class == superclass) { + if (superclass == null || superclass == Object.class) { return null; } return findAnnotation(superclass, annotationType, visited); @@ -793,7 +812,7 @@ public abstract class AnnotationUtils { */ @Nullable public static Class findAnnotationDeclaringClass(Class annotationType, @Nullable Class clazz) { - if (clazz == null || Object.class == clazz) { + if (clazz == null || clazz == Object.class) { return null; } if (isAnnotationDeclaredLocally(annotationType, clazz)) { @@ -827,8 +846,10 @@ public abstract class AnnotationUtils { * @see #isAnnotationDeclaredLocally(Class, Class) */ @Nullable - public static Class findAnnotationDeclaringClassForTypes(List> annotationTypes, @Nullable Class clazz) { - if (clazz == null || Object.class == clazz) { + public static Class findAnnotationDeclaringClassForTypes( + List> annotationTypes, @Nullable Class clazz) { + + if (clazz == null || clazz == Object.class) { return null; } for (Class annotationType : annotationTypes) {