Avoid unnecessary introspection on methods and meta-annotations
Issue: SPR-16667
This commit is contained in:
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -431,7 +431,8 @@ class ConfigurationClassEnhancer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMatch(Method candidateMethod) {
|
public boolean isMatch(Method candidateMethod) {
|
||||||
return BeanAnnotationHelper.isBeanAnnotated(candidateMethod);
|
return (candidateMethod.getDeclaringClass() != Object.class &&
|
||||||
|
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {
|
private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
import org.springframework.core.BridgeMethodResolver;
|
import org.springframework.core.BridgeMethodResolver;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
@@ -232,7 +233,6 @@ public class AnnotatedElementUtils {
|
|||||||
|
|
||||||
return Boolean.TRUE.equals(
|
return Boolean.TRUE.equals(
|
||||||
searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() {
|
searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Boolean process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
public Boolean process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
||||||
@@ -950,7 +950,7 @@ public class AnnotatedElementUtils {
|
|||||||
// Recursively search in meta-annotations
|
// Recursively search in meta-annotations
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
||||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
|
if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) {
|
||||||
T result = searchWithGetSemantics(currentAnnotationType, annotationType,
|
T result = searchWithGetSemantics(currentAnnotationType, annotationType,
|
||||||
annotationName, containerType, processor, visited, metaDepth + 1);
|
annotationName, containerType, processor, visited, metaDepth + 1);
|
||||||
if (result != null) {
|
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) {
|
for (Annotation annotation : annotations) {
|
||||||
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
||||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
|
if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) {
|
||||||
T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName,
|
T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName,
|
||||||
containerType, processor, visited, metaDepth + 1);
|
containerType, processor, visited, metaDepth + 1);
|
||||||
if (result != null) {
|
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
|
// Prepend to support top-down ordering within class hierarchies
|
||||||
processor.getAggregatedResults().addAll(0, aggregatedResults);
|
processor.getAggregatedResults().addAll(0, aggregatedResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element instanceof Method) {
|
if (element instanceof Method) {
|
||||||
Method method = (Method) element;
|
Method method = (Method) element;
|
||||||
|
T result;
|
||||||
|
|
||||||
// Search on possibly bridged method
|
// Search on possibly bridged method
|
||||||
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||||
T result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName, containerType,
|
if (resolvedMethod != method) {
|
||||||
processor, visited, metaDepth);
|
result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName,
|
||||||
if (result != null) {
|
containerType, processor, visited, metaDepth);
|
||||||
return result;
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search on methods in interfaces declared locally
|
// Search on methods in interfaces declared locally
|
||||||
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
|
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
|
||||||
result = searchOnInterfaces(method, annotationType, annotationName, containerType, processor,
|
if (ifcs.length > 0) {
|
||||||
visited, metaDepth, ifcs);
|
result = searchOnInterfaces(method, annotationType, annotationName, containerType,
|
||||||
if (result != null) {
|
processor, visited, metaDepth, ifcs);
|
||||||
return result;
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search on methods in class hierarchy and interface hierarchy
|
// Search on methods in class hierarchy and interface hierarchy
|
||||||
@@ -1189,10 +1194,10 @@ public class AnnotatedElementUtils {
|
|||||||
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
|
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
|
||||||
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
|
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
|
||||||
|
|
||||||
for (Class<?> iface : ifcs) {
|
for (Class<?> ifc : ifcs) {
|
||||||
if (AnnotationUtils.isInterfaceWithAnnotatedMethods(iface)) {
|
if (AnnotationUtils.isInterfaceWithAnnotatedMethods(ifc)) {
|
||||||
try {
|
try {
|
||||||
Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());
|
Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes());
|
||||||
T result = searchWithFindSemantics(equivalentMethod, annotationType, annotationName, containerType,
|
T result = searchWithFindSemantics(equivalentMethod, annotationType, annotationName, containerType,
|
||||||
processor, visited, metaDepth);
|
processor, visited, metaDepth);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
@@ -1208,6 +1213,26 @@ public class AnnotatedElementUtils {
|
|||||||
return null;
|
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}
|
* Get the array of raw (unsynthesized) annotations from the {@code value}
|
||||||
* attribute of the supplied repeatable annotation {@code container}.
|
* 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.core.BridgeMethodResolver;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
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.
|
* <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
|
* For support for arbitrary levels of meta-annotations, use one of the
|
||||||
* {@code find*()} methods instead.
|
* {@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
|
* @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
|
* @return the first matching annotation, or {@code null} if not found
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Nullable
|
@Nullable
|
||||||
public static <A extends Annotation> A getAnnotation(Annotation ann, Class<A> annotationType) {
|
public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) {
|
||||||
if (annotationType.isInstance(ann)) {
|
if (annotationType.isInstance(annotation)) {
|
||||||
return synthesizeAnnotation((A) ann);
|
return synthesizeAnnotation((A) annotation);
|
||||||
}
|
}
|
||||||
Class<? extends Annotation> annotatedElement = ann.annotationType();
|
Class<? extends Annotation> annotatedElement = annotation.annotationType();
|
||||||
try {
|
try {
|
||||||
return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement);
|
return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement);
|
||||||
}
|
}
|
||||||
@@ -574,10 +575,10 @@ public abstract class AnnotationUtils {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
|
private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
|
||||||
A annotation = null;
|
A annotation = null;
|
||||||
for (Class<?> iface : ifcs) {
|
for (Class<?> ifc : ifcs) {
|
||||||
if (isInterfaceWithAnnotatedMethods(iface)) {
|
if (isInterfaceWithAnnotatedMethods(ifc)) {
|
||||||
try {
|
try {
|
||||||
Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());
|
Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes());
|
||||||
annotation = getAnnotation(equivalentMethod, annotationType);
|
annotation = getAnnotation(equivalentMethod, annotationType);
|
||||||
}
|
}
|
||||||
catch (NoSuchMethodException ex) {
|
catch (NoSuchMethodException ex) {
|
||||||
@@ -591,15 +592,20 @@ public abstract class AnnotationUtils {
|
|||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
|
static boolean isInterfaceWithAnnotatedMethods(Class<?> ifc) {
|
||||||
Boolean found = annotatedInterfaceCache.get(iface);
|
if (ClassUtils.isJavaLanguageInterface(ifc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean found = annotatedInterfaceCache.get(ifc);
|
||||||
if (found != null) {
|
if (found != null) {
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
found = Boolean.FALSE;
|
found = Boolean.FALSE;
|
||||||
for (Method ifcMethod : iface.getMethods()) {
|
for (Method ifcMethod : ifc.getMethods()) {
|
||||||
try {
|
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;
|
found = Boolean.TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -608,7 +614,7 @@ public abstract class AnnotationUtils {
|
|||||||
handleIntrospectionFailure(ifcMethod, ex);
|
handleIntrospectionFailure(ifcMethod, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
annotatedInterfaceCache.put(iface, found);
|
annotatedInterfaceCache.put(ifc, found);
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,8 @@ public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter
|
|||||||
}
|
}
|
||||||
else if (typeName.startsWith("java")) {
|
else if (typeName.startsWith("java")) {
|
||||||
if (!this.annotationType.getName().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;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.Ordered;
|
||||||
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
|
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
@@ -1541,6 +1542,13 @@ public class AnnotationUtilsTests {
|
|||||||
assertArrayEquals(new char[] { 'x', 'y', 'z' }, chars);
|
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
|
@SafeVarargs
|
||||||
static <T> T[] asArray(T... arr) {
|
static <T> T[] asArray(T... arr) {
|
||||||
@@ -1634,6 +1642,12 @@ public class AnnotationUtilsTests {
|
|||||||
void fromInterfaceImplementedByRoot();
|
void fromInterfaceImplementedByRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface NullableAnnotatedInterface {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
void fromInterfaceImplementedByRoot();
|
||||||
|
}
|
||||||
|
|
||||||
public static class Root implements AnnotatedInterface {
|
public static class Root implements AnnotatedInterface {
|
||||||
|
|
||||||
@Order(27)
|
@Order(27)
|
||||||
|
|||||||
Reference in New Issue
Block a user