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 c10e3d98c1ae..cf5eac08aa98 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. @@ -431,7 +431,8 @@ private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs, @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 0bd78ec55911..41b830324155 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 @@ -30,6 +30,7 @@ import org.springframework.core.BridgeMethodResolver; import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -232,7 +233,6 @@ private static boolean hasMetaAnnotationTypes(AnnotatedElement element, @Nullabl return Boolean.TRUE.equals( searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor() { - @Override @Nullable public Boolean process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { @@ -950,7 +950,7 @@ else if (currentAnnotationType == containerType) { // Recursively search in meta-annotations for (Annotation annotation : annotations) { Class currentAnnotationType = annotation.annotationType(); - if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { + if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) { T result = searchWithGetSemantics(currentAnnotationType, annotationType, annotationName, containerType, processor, visited, metaDepth + 1); if (result != null) { @@ -1083,10 +1083,10 @@ else if (currentAnnotationType == containerType) { } } - // Search in meta annotations on local annotations + // Recursively search in meta-annotations for (Annotation annotation : annotations) { Class currentAnnotationType = annotation.annotationType(); - if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { + if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) { T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName, containerType, processor, visited, metaDepth + 1); if (result != null) { @@ -1101,28 +1101,33 @@ else if (currentAnnotationType == containerType) { } } - if (aggregatedResults != null) { + if (!CollectionUtils.isEmpty(aggregatedResults)) { // 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 @@ -1189,10 +1194,10 @@ private static T searchOnInterfaces(Method method, @Nullable Class containerType, Processor processor, Set visited, int metaDepth, Class[] ifcs) { - for (Class iface : ifcs) { - if (AnnotationUtils.isInterfaceWithAnnotatedMethods(iface)) { + for (Class ifc : ifcs) { + if (AnnotationUtils.isInterfaceWithAnnotatedMethods(ifc)) { try { - Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes()); + Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes()); T result = searchWithFindSemantics(equivalentMethod, annotationType, annotationName, containerType, processor, visited, metaDepth); if (result != null) { @@ -1208,6 +1213,26 @@ private static T searchOnInterfaces(Method method, @Nullable Class currentAnnotationType, + @Nullable Class annotationType, @Nullable String annotationName) { + + if (AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { + return false; + } + if (currentAnnotationType == Nullable.class || currentAnnotationType.getName().startsWith("java")) { + // @Nullable and standard Java annotations are only meant to have standard Java meta-annotations + // -> not worth searching otherwise. + return ((annotationType != null && annotationType.getName().startsWith("java")) || + (annotationName != null && annotationName.startsWith("java"))); + } + return true; + } + /** * Get the array of raw (unsynthesized) annotations from the {@code value} * attribute of the supplied repeatable annotation {@code container}. 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 2c296bbd5f1d..29b7d63c0a55 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 @@ -40,6 +40,7 @@ import org.springframework.core.BridgeMethodResolver; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; @@ -147,18 +148,18 @@ 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") @Nullable - 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); } @@ -574,10 +575,10 @@ public static A findAnnotation(Method method, @Nullable C @Nullable 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) { @@ -591,15 +592,20 @@ private static A searchOnInterfaces(Method method, Class< return annotation; } - static boolean isInterfaceWithAnnotatedMethods(Class iface) { - Boolean found = annotatedInterfaceCache.get(iface); + static boolean isInterfaceWithAnnotatedMethods(Class ifc) { + if (ClassUtils.isJavaLanguageInterface(ifc)) { + return false; + } + + 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) { + Annotation[] anns = ifcMethod.getAnnotations(); + if (anns.length > 1 || (anns.length == 1 && anns[0].annotationType() != Nullable.class)) { found = Boolean.TRUE; break; } @@ -608,7 +614,7 @@ static boolean isInterfaceWithAnnotatedMethods(Class iface) { handleIntrospectionFailure(ifcMethod, ex); } } - annotatedInterfaceCache.put(iface, found); + annotatedInterfaceCache.put(ifc, found); return found; } 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 664d477f9f83..e69facab991c 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 @@ -114,7 +114,8 @@ protected Boolean hasAnnotation(String typeName) { } else if (typeName.startsWith("java")) { if (!this.annotationType.getName().startsWith("java")) { - // Standard Java classes don't have non-standard annotations on them. + // Standard Java types do not have non-standard annotations on them -> + // skip any load attempt, in particular for Java language interfaces. return false; } try { 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 92e00fccb192..1d720552ea24 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. @@ -36,6 +36,7 @@ import org.springframework.core.Ordered; import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -1541,6 +1542,13 @@ public void synthesizeAnnotationWithArrayOfChars() throws Exception { assertArrayEquals(new char[] { 'x', 'y', 'z' }, chars); } + @Test + public void interfaceWithAnnotatedMethods() { + assertFalse(AnnotationUtils.isInterfaceWithAnnotatedMethods(NonAnnotatedInterface.class)); + assertTrue(AnnotationUtils.isInterfaceWithAnnotatedMethods(AnnotatedInterface.class)); + assertFalse(AnnotationUtils.isInterfaceWithAnnotatedMethods(NullableAnnotatedInterface.class)); + } + @SafeVarargs static T[] asArray(T... arr) { @@ -1634,6 +1642,12 @@ public interface AnnotatedInterface { void fromInterfaceImplementedByRoot(); } + public interface NullableAnnotatedInterface { + + @Nullable + void fromInterfaceImplementedByRoot(); + } + public static class Root implements AnnotatedInterface { @Order(27)