Avoid unnecessary introspection on methods and meta-annotations
Issue: SPR-16667
This commit is contained in:
parent
b1048975d2
commit
4da27c2a73
|
|
@ -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 @@ 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) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import java.util.Set;
|
|||
|
||||
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 @@ public class AnnotatedElementUtils {
|
|||
|
||||
return Boolean.TRUE.equals(
|
||||
searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Boolean process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
||||
|
|
@ -950,7 +950,7 @@ public class AnnotatedElementUtils {
|
|||
// Recursively search in meta-annotations
|
||||
for (Annotation annotation : annotations) {
|
||||
Class<? extends Annotation> 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 @@ public class AnnotatedElementUtils {
|
|||
}
|
||||
}
|
||||
|
||||
// Search in meta annotations on local annotations
|
||||
// Recursively search in meta-annotations
|
||||
for (Annotation annotation : annotations) {
|
||||
Class<? extends Annotation> 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 @@ public class AnnotatedElementUtils {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ public class AnnotatedElementUtils {
|
|||
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
|
||||
Processor<T> processor, Set<AnnotatedElement> 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 @@ public class AnnotatedElementUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the current annotation type is generally expected to have
|
||||
* meta-annotations of the specified annotation type that we're searching for,
|
||||
* explicitly excluding some common cases that would never deliver any results.
|
||||
*/
|
||||
private static boolean hasSearchableMetaAnnotations(Class<? extends Annotation> 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}.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
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 {
|
|||
* <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")
|
||||
@Nullable
|
||||
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);
|
||||
}
|
||||
|
|
@ -574,10 +575,10 @@ public abstract class AnnotationUtils {
|
|||
@Nullable
|
||||
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) {
|
||||
|
|
@ -591,15 +592,20 @@ public abstract class AnnotationUtils {
|
|||
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 @@ public abstract class AnnotationUtils {
|
|||
handleIntrospectionFailure(ifcMethod, ex);
|
||||
}
|
||||
}
|
||||
annotatedInterfaceCache.put(iface, found);
|
||||
annotatedInterfaceCache.put(ifc, found);
|
||||
return found;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,8 @@ public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter
|
|||
}
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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.junit.rules.ExpectedException;
|
|||
|
||||
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 class AnnotationUtilsTests {
|
|||
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> T[] asArray(T... arr) {
|
||||
|
|
@ -1634,6 +1642,12 @@ public class AnnotationUtilsTests {
|
|||
void fromInterfaceImplementedByRoot();
|
||||
}
|
||||
|
||||
public interface NullableAnnotatedInterface {
|
||||
|
||||
@Nullable
|
||||
void fromInterfaceImplementedByRoot();
|
||||
}
|
||||
|
||||
public static class Root implements AnnotatedInterface {
|
||||
|
||||
@Order(27)
|
||||
|
|
|
|||
Loading…
Reference in New Issue