Comprehensively cache annotated methods for interfaces and superclasses
Issue: SPR-16675
This commit is contained in:
@@ -1050,17 +1050,48 @@ public class AnnotatedElementUtils {
|
||||
try {
|
||||
// Locally declared annotations (ignoring @Inherited)
|
||||
Annotation[] annotations = element.getDeclaredAnnotations();
|
||||
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);
|
||||
if (annotations.length > 0) {
|
||||
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);
|
||||
|
||||
// Search in local annotations
|
||||
for (Annotation annotation : annotations) {
|
||||
Class<? extends Annotation> 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<? extends Annotation> 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<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
||||
if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) {
|
||||
T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName,
|
||||
containerType, processor, visited, metaDepth + 1);
|
||||
if (result != null) {
|
||||
processor.postProcess(currentAnnotationType, annotation, result);
|
||||
if (aggregatedResults != null && metaDepth == 0) {
|
||||
aggregatedResults.add(result);
|
||||
}
|
||||
@@ -1069,43 +1100,14 @@ 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 (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<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
||||
if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) {
|
||||
T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName,
|
||||
containerType, processor, visited, metaDepth + 1);
|
||||
if (result != null) {
|
||||
processor.postProcess(currentAnnotationType, annotation, result);
|
||||
if (aggregatedResults != null && 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 (!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;
|
||||
@@ -1123,8 +1125,8 @@ public class AnnotatedElementUtils {
|
||||
// Search on methods in interfaces declared locally
|
||||
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
|
||||
if (ifcs.length > 0) {
|
||||
result = searchOnInterfaces(method, annotationType, annotationName, containerType,
|
||||
processor, visited, metaDepth, ifcs);
|
||||
result = searchOnInterfaces(method, annotationType, annotationName,
|
||||
containerType, processor, visited, metaDepth, ifcs);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
@@ -1137,23 +1139,23 @@ public class AnnotatedElementUtils {
|
||||
if (clazz == null || Object.class == clazz) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
|
||||
Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod);
|
||||
result = searchWithFindSemantics(resolvedEquivalentMethod, annotationType, annotationName,
|
||||
containerType, processor, visited, metaDepth);
|
||||
if (result != null) {
|
||||
return result;
|
||||
Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
|
||||
if (!annotatedMethods.isEmpty()) {
|
||||
for (Method annotatedMethod : annotatedMethods) {
|
||||
if (annotatedMethod.getName().equals(method.getName()) &&
|
||||
Arrays.equals(annotatedMethod.getParameterTypes(), method.getParameterTypes())) {
|
||||
Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod);
|
||||
result = searchWithFindSemantics(resolvedSuperMethod, annotationType, annotationName,
|
||||
containerType, processor, visited, metaDepth);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -1164,8 +1166,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;
|
||||
}
|
||||
@@ -1174,8 +1176,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;
|
||||
}
|
||||
@@ -1195,18 +1197,18 @@ public class AnnotatedElementUtils {
|
||||
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
|
||||
|
||||
for (Class<?> ifc : ifcs) {
|
||||
if (AnnotationUtils.isInterfaceWithAnnotatedMethods(ifc)) {
|
||||
try {
|
||||
Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes());
|
||||
T result = searchWithFindSemantics(equivalentMethod, annotationType, annotationName, containerType,
|
||||
processor, visited, metaDepth);
|
||||
if (result != null) {
|
||||
return result;
|
||||
Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(ifc);
|
||||
if (!annotatedMethods.isEmpty()) {
|
||||
for (Method annotatedMethod : annotatedMethods) {
|
||||
if (annotatedMethod.getName().equals(method.getName()) &&
|
||||
Arrays.equals(annotatedMethod.getParameterTypes(), method.getParameterTypes())) {
|
||||
T result = searchWithFindSemantics(annotatedMethod, annotationType, annotationName,
|
||||
containerType, processor, visited, metaDepth);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Skip this interface - it doesn't have the method...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -122,7 +123,7 @@ public abstract class AnnotationUtils {
|
||||
private static final Map<AnnotationCacheKey, Boolean> metaPresentCache =
|
||||
new ConcurrentReferenceHashMap<>(256);
|
||||
|
||||
private static final Map<Class<?>, Boolean> annotatedInterfaceCache =
|
||||
private static final Map<Class<?>, Set<Method>> annotatedBaseTypeCache =
|
||||
new ConcurrentReferenceHashMap<>(256);
|
||||
|
||||
private static final Map<Class<? extends Annotation>, Boolean> synthesizableCache =
|
||||
@@ -539,7 +540,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());
|
||||
}
|
||||
@@ -574,48 +574,63 @@ public abstract class AnnotationUtils {
|
||||
|
||||
@Nullable
|
||||
private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
|
||||
A annotation = null;
|
||||
for (Class<?> ifc : ifcs) {
|
||||
if (isInterfaceWithAnnotatedMethods(ifc)) {
|
||||
try {
|
||||
Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes());
|
||||
annotation = getAnnotation(equivalentMethod, annotationType);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Skip this interface - it doesn't have the method...
|
||||
}
|
||||
if (annotation != null) {
|
||||
break;
|
||||
Set<Method> annotatedMethods = getAnnotatedMethodsInBaseType(ifc);
|
||||
if (!annotatedMethods.isEmpty()) {
|
||||
for (Method annotatedMethod : annotatedMethods) {
|
||||
if (annotatedMethod.getName().equals(method.getName()) &&
|
||||
Arrays.equals(annotatedMethod.getParameterTypes(), method.getParameterTypes())) {
|
||||
A annotation = getAnnotation(annotatedMethod, annotationType);
|
||||
if (annotation != null) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return annotation;
|
||||
return null;
|
||||
}
|
||||
|
||||
static boolean isInterfaceWithAnnotatedMethods(Class<?> ifc) {
|
||||
if (ClassUtils.isJavaLanguageInterface(ifc)) {
|
||||
return false;
|
||||
static Set<Method> getAnnotatedMethodsInBaseType(Class<?> baseType) {
|
||||
if (ClassUtils.isJavaLanguageInterface(baseType)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Boolean found = annotatedInterfaceCache.get(ifc);
|
||||
if (found != null) {
|
||||
return found;
|
||||
Set<Method> annotatedMethods = annotatedBaseTypeCache.get(baseType);
|
||||
if (annotatedMethods != null) {
|
||||
return annotatedMethods;
|
||||
}
|
||||
found = Boolean.FALSE;
|
||||
for (Method ifcMethod : ifc.getMethods()) {
|
||||
Method[] methods = (baseType.isInterface() ? baseType.getMethods() : baseType.getDeclaredMethods());
|
||||
for (Method baseMethod : methods) {
|
||||
try {
|
||||
Annotation[] anns = ifcMethod.getAnnotations();
|
||||
if (anns.length > 1 || (anns.length == 1 && anns[0].annotationType() != Nullable.class)) {
|
||||
found = Boolean.TRUE;
|
||||
break;
|
||||
if (hasSearchableAnnotations(baseMethod)) {
|
||||
if (annotatedMethods == null) {
|
||||
annotatedMethods = new HashSet<>();
|
||||
}
|
||||
annotatedMethods.add(baseMethod);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleIntrospectionFailure(ifcMethod, ex);
|
||||
handleIntrospectionFailure(baseMethod, ex);
|
||||
}
|
||||
}
|
||||
annotatedInterfaceCache.put(ifc, found);
|
||||
return found;
|
||||
if (annotatedMethods == null) {
|
||||
annotatedMethods = Collections.emptySet();
|
||||
}
|
||||
annotatedBaseTypeCache.put(baseType, annotatedMethods);
|
||||
return annotatedMethods;
|
||||
}
|
||||
|
||||
private static boolean hasSearchableAnnotations(Method ifcMethod) {
|
||||
Annotation[] anns = ifcMethod.getAnnotations();
|
||||
if (anns.length == 0) {
|
||||
return false;
|
||||
}
|
||||
if (anns.length == 1) {
|
||||
Class<?> annType = anns[0].annotationType();
|
||||
return (annType != Nullable.class && annType != Deprecated.class);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1875,6 +1890,20 @@ public abstract class AnnotationUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the internal annotation metadata cache.
|
||||
* @since 4.3.15
|
||||
*/
|
||||
public static void clearCache() {
|
||||
findAnnotationCache.clear();
|
||||
metaPresentCache.clear();
|
||||
annotatedBaseTypeCache.clear();
|
||||
synthesizableCache.clear();
|
||||
attributeAliasesCache.clear();
|
||||
attributeMethodsCache.clear();
|
||||
aliasDescriptorCache.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cache key for the AnnotatedElement cache.
|
||||
|
||||
Reference in New Issue
Block a user