Consistently skip unnecessary search on superclasses and empty elements

Includes caching of declared annotation arrays and combined searching for several annotation types (used in SpringCacheAnnotationParser).

Issue: SPR-16933
This commit is contained in:
Juergen Hoeller
2018-08-15 17:30:14 +02:00
parent 50dc8c2358
commit 109a2b49e5
6 changed files with 292 additions and 270 deletions

View File

@@ -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;
@@ -180,7 +181,7 @@ public abstract class AnnotatedElementUtils {
try {
final Set<String> types = new LinkedHashSet<>();
searchWithGetSemantics(composed.annotationType(), null, null, null, new SimpleAnnotationProcessor<Object>(true) {
searchWithGetSemantics(composed.annotationType(), Collections.emptySet(), null, null, new SimpleAnnotationProcessor<Object>(true) {
@Override
@Nullable
public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
@@ -391,8 +392,8 @@ public abstract class AnnotatedElementUtils {
return AnnotationUtils.synthesizeAnnotation(annotation, element);
}
// Shortcut: no non-java annotations to be found on plain Java classes and org.springframework.lang types...
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element) && !annotationType.getName().startsWith("java")) {
// Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types...
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) {
return null;
}
@@ -424,7 +425,31 @@ public abstract class AnnotatedElementUtils {
public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithGetSemantics(element, annotationType, null, processor);
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
/**
* Get <strong>all</strong> annotations of the specified {@code annotationTypes}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the
* annotation hierarchy and synthesize the results back into an annotation
* of the corresponding {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationTypes the annotation types to find
* @return the set of all merged, synthesized {@code Annotations} found,
* or an empty set if none were found
* @since 5.1
* @see #getAllMergedAnnotations(AnnotatedElement, Class)
*/
public static Set<Annotation> getAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) {
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithGetSemantics(element, annotationTypes, null, null, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
/**
@@ -494,8 +519,8 @@ public abstract class AnnotatedElementUtils {
}
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithGetSemantics(element, annotationType, null, containerType, processor);
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
searchWithGetSemantics(element, Collections.singleton(annotationType), null, containerType, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
/**
@@ -677,8 +702,8 @@ public abstract class AnnotatedElementUtils {
return AnnotationUtils.synthesizeAnnotation(annotation, element);
}
// Shortcut: no non-java annotations to be found on plain Java classes and org.springframework.lang types...
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element) && !annotationType.getName().startsWith("java")) {
// Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types...
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) {
return null;
}
@@ -709,7 +734,31 @@ public abstract class AnnotatedElementUtils {
public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithFindSemantics(element, annotationType, null, processor);
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
/**
* Find <strong>all</strong> annotations of the specified {@code annotationTypes}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the
* annotation hierarchy and synthesize the results back into an annotation
* of the corresponding {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationTypes the annotation types to find
* @return the set of all merged, synthesized {@code Annotations} found,
* or an empty set if none were found
* @since 5.1
* @see #findAllMergedAnnotations(AnnotatedElement, Class)
*/
public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) {
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithFindSemantics(element, annotationTypes, null, null, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
/**
@@ -779,8 +828,8 @@ public abstract class AnnotatedElementUtils {
}
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithFindSemantics(element, annotationType, null, containerType, processor);
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
searchWithFindSemantics(element, Collections.singleton(annotationType), null, containerType, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
/**
@@ -799,7 +848,9 @@ public abstract class AnnotatedElementUtils {
@Nullable Class<? extends Annotation> annotationType,
@Nullable String annotationName, Processor<T> processor) {
return searchWithGetSemantics(element, annotationType, annotationName, null, processor);
return searchWithGetSemantics(element,
(annotationType != null ? Collections.singleton(annotationType) : Collections.emptySet()),
annotationName, null, processor);
}
/**
@@ -807,7 +858,7 @@ public abstract class AnnotatedElementUtils {
* {@code annotationType} on the specified {@code element}, following
* <em>get semantics</em>.
* @param element the annotated element
* @param annotationType the annotation type to find
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
@@ -818,11 +869,11 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithGetSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor) {
try {
return searchWithGetSemantics(element, annotationType, annotationName, containerType, processor,
return searchWithGetSemantics(element, annotationTypes, annotationName, containerType, processor,
new HashSet<>(), 0);
}
catch (Throwable ex) {
@@ -838,7 +889,7 @@ public abstract class AnnotatedElementUtils {
* <p>The {@code metaDepth} parameter is explained in the
* {@link Processor#process process()} method of the {@link Processor} API.
* @param element the annotated element
* @param annotationType the annotation type to find
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
@@ -850,33 +901,35 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithGetSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> visited, int metaDepth) {
if (visited.add(element)) {
try {
// Start searching within locally declared annotations
List<Annotation> declaredAnnotations = Arrays.asList(element.getDeclaredAnnotations());
List<Annotation> declaredAnnotations = Arrays.asList(AnnotationUtils.getDeclaredAnnotations(element));
T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations,
annotationType, annotationName, containerType, processor, visited, metaDepth);
annotationTypes, annotationName, containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new
List<Annotation> 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<Annotation> inheritedAnnotations = new LinkedList<>();
for (Annotation annotation : element.getAnnotations()) {
if (!declaredAnnotations.contains(annotation)) {
inheritedAnnotations.add(annotation);
}
}
// Continue searching within inherited annotations
result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations,
annotationTypes, 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;
}
}
}
@@ -899,7 +952,7 @@ public abstract class AnnotatedElementUtils {
* @param element the element that is annotated with the supplied
* annotations, used for contextual logging; may be {@code null} if unknown
* @param annotations the annotations to search in
* @param annotationType the annotation type to find
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
@@ -912,7 +965,7 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element,
List<Annotation> annotations, @Nullable Class<? extends Annotation> annotationType,
List<Annotation> annotations, Set<Class<? extends Annotation>> annotationTypes,
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
@@ -920,7 +973,7 @@ public abstract class AnnotatedElementUtils {
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
if (currentAnnotationType == annotationType ||
if (annotationTypes.contains(currentAnnotationType) ||
currentAnnotationType.getName().equals(annotationName) ||
processor.alwaysProcesses()) {
T result = processor.process(element, annotation, metaDepth);
@@ -950,8 +1003,8 @@ public abstract class AnnotatedElementUtils {
// Recursively search in meta-annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) {
T result = searchWithGetSemantics(currentAnnotationType, annotationType,
if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
T result = searchWithGetSemantics(currentAnnotationType, annotationTypes,
annotationName, containerType, processor, visited, metaDepth + 1);
if (result != null) {
processor.postProcess(element, annotation, result);
@@ -985,7 +1038,9 @@ public abstract class AnnotatedElementUtils {
@Nullable Class<? extends Annotation> annotationType,
@Nullable String annotationName, Processor<T> processor) {
return searchWithFindSemantics(element, annotationType, annotationName, null, processor);
return searchWithFindSemantics(element,
(annotationType != null ? Collections.singleton(annotationType) : Collections.emptySet()),
annotationName, null, processor);
}
/**
@@ -993,7 +1048,7 @@ public abstract class AnnotatedElementUtils {
* {@code annotationType} on the specified {@code element}, following
* <em>find semantics</em>.
* @param element the annotated element
* @param annotationType the annotation type to find
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
@@ -1004,7 +1059,7 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithFindSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor) {
if (containerType != null && !processor.aggregates()) {
@@ -1014,7 +1069,7 @@ public abstract class AnnotatedElementUtils {
try {
return searchWithFindSemantics(
element, annotationType, annotationName, containerType, processor, new HashSet<>(), 0);
element, annotationTypes, annotationName, containerType, processor, new HashSet<>(), 0);
}
catch (Throwable ex) {
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
@@ -1029,7 +1084,7 @@ public abstract class AnnotatedElementUtils {
* <p>The {@code metaDepth} parameter is explained in the
* {@link Processor#process process()} method of the {@link Processor} API.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
@@ -1042,14 +1097,14 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithFindSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> visited, int metaDepth) {
if (visited.add(element)) {
try {
// Locally declared annotations (ignoring @Inherited)
Annotation[] annotations = element.getDeclaredAnnotations();
Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(element);
if (annotations.length > 0) {
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);
@@ -1057,7 +1112,7 @@ public abstract class AnnotatedElementUtils {
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
if (currentAnnotationType == annotationType ||
if (annotationTypes.contains(currentAnnotationType) ||
currentAnnotationType.getName().equals(annotationName) ||
processor.alwaysProcesses()) {
T result = processor.process(element, annotation, metaDepth);
@@ -1087,8 +1142,8 @@ public abstract class AnnotatedElementUtils {
// 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,
if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
T result = searchWithFindSemantics(currentAnnotationType, annotationTypes, annotationName,
containerType, processor, visited, metaDepth + 1);
if (result != null) {
processor.postProcess(currentAnnotationType, annotation, result);
@@ -1115,7 +1170,7 @@ public abstract class AnnotatedElementUtils {
// Search on possibly bridged method
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (resolvedMethod != method) {
result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName,
result = searchWithFindSemantics(resolvedMethod, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
@@ -1125,7 +1180,7 @@ public abstract class AnnotatedElementUtils {
// Search on methods in interfaces declared locally
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
if (ifcs.length > 0) {
result = searchOnInterfaces(method, annotationType, annotationName,
result = searchOnInterfaces(method, annotationTypes, annotationName,
containerType, processor, visited, metaDepth, ifcs);
if (result != null) {
return result;
@@ -1136,7 +1191,7 @@ public abstract 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<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
@@ -1144,8 +1199,8 @@ public abstract class AnnotatedElementUtils {
for (Method annotatedMethod : annotatedMethods) {
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod);
result = searchWithFindSemantics(resolvedSuperMethod, annotationType, annotationName,
containerType, processor, visited, metaDepth);
result = searchWithFindSemantics(resolvedSuperMethod, annotationTypes,
annotationName, containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
@@ -1153,7 +1208,7 @@ public abstract class AnnotatedElementUtils {
}
}
// Search on interfaces declared on superclass
result = searchOnInterfaces(method, annotationType, annotationName,
result = searchOnInterfaces(method, annotationTypes, annotationName,
containerType, processor, visited, metaDepth, clazz.getInterfaces());
if (result != null) {
return result;
@@ -1162,23 +1217,23 @@ public abstract 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, annotationTypes, 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, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
@@ -1191,7 +1246,7 @@ public abstract class AnnotatedElementUtils {
}
@Nullable
private static <T> T searchOnInterfaces(Method method, @Nullable Class<? extends Annotation> annotationType,
private static <T> T searchOnInterfaces(Method method, Set<Class<? extends Annotation>> annotationTypes,
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
@@ -1200,7 +1255,7 @@ public abstract class AnnotatedElementUtils {
if (!annotatedMethods.isEmpty()) {
for (Method annotatedMethod : annotatedMethods) {
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
T result = searchWithFindSemantics(annotatedMethod, annotationType, annotationName,
T result = searchWithFindSemantics(annotatedMethod, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
@@ -1213,26 +1268,6 @@ public abstract 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 (AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
// @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}.
@@ -1303,13 +1338,23 @@ public abstract class AnnotatedElementUtils {
}
}
private static <A extends Annotation> Set<A> postProcessAndSynthesizeAggregatedResults(AnnotatedElement element,
Class<A> annotationType, List<AnnotationAttributes> aggregatedResults) {
/**
* Post-process the aggregated results into a set of synthesized annotations.
* @param element the annotated element
* @param aggregatedResults the aggregated results for the given element
* @return the set of annotations
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> Set<A> postProcessAndSynthesizeAggregatedResults(
AnnotatedElement element, List<AnnotationAttributes> aggregatedResults) {
Set<A> annotations = new LinkedHashSet<>();
for (AnnotationAttributes attributes : aggregatedResults) {
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
annotations.add(AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element));
Class<? extends Annotation> annType = attributes.annotationType();
if (annType != null) {
annotations.add((A) AnnotationUtils.synthesizeAnnotation(attributes, annType, element));
}
}
return annotations;
}

View File

@@ -125,6 +125,9 @@ public abstract class AnnotationUtils {
private static final Map<AnnotationCacheKey, Boolean> metaPresentCache =
new ConcurrentReferenceHashMap<>(256);
private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationsCache =
new ConcurrentReferenceHashMap<>(256);
private static final Map<Class<?>, Set<Method>> annotatedBaseTypeCache =
new ConcurrentReferenceHashMap<>(256);
@@ -341,18 +344,13 @@ public abstract class AnnotationUtils {
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
Set<A> annotations = getDeclaredRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType);
if (!annotations.isEmpty()) {
return annotations;
}
if (annotatedElement instanceof Class) {
if (annotations.isEmpty() && 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);
}
}
return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, false);
return annotations;
}
/**
@@ -422,37 +420,11 @@ public abstract class AnnotationUtils {
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement,
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, true);
}
/**
* Perform the actual work for {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}
* and {@link #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)}.
* <p>Correctly handles <em>bridge methods</em> generated by the
* compiler if the supplied element is a {@link Method}.
* <p>Meta-annotations will be searched if the annotation is not
* <em>present</em> on the supplied element.
* @param annotatedElement the element to look for annotations on
* @param annotationType the annotation type to look for
* @param containerAnnotationType the type of the container that holds
* the annotations; may be {@code null} if a container is not supported
* or if it should be looked up via @{@link java.lang.annotation.Repeatable}
* when running on Java 8 or higher
* @param declaredMode {@code true} if only declared annotations (i.e.,
* directly or indirectly present) should be considered
* @return the annotations found or an empty set (never {@code null})
* @since 4.2
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
* @see java.lang.annotation.Repeatable
*/
private static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement,
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType, boolean declaredMode) {
try {
if (annotatedElement instanceof Method) {
annotatedElement = BridgeMethodResolver.findBridgedMethod((Method) annotatedElement);
}
return new AnnotationCollector<>(annotationType, containerAnnotationType, declaredMode).getResult(annotatedElement);
return new AnnotationCollector<>(annotationType, containerAnnotationType).getResult(annotatedElement);
}
catch (Throwable ex) {
handleIntrospectionFailure(annotatedElement, ex);
@@ -502,7 +474,7 @@ public abstract class AnnotationUtils {
if (annotation != null) {
return annotation;
}
for (Annotation declaredAnn : annotatedElement.getDeclaredAnnotations()) {
for (Annotation declaredAnn : getDeclaredAnnotations(annotatedElement)) {
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
annotation = findAnnotation((AnnotatedElement) declaredType, annotationType, visited);
@@ -554,7 +526,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<Method> annotatedMethods = getAnnotatedMethodsInBaseType(clazz);
@@ -601,6 +573,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<Method> getAnnotatedMethodsInBaseType(Class<?> baseType) {
boolean ifcCheck = baseType.isInterface();
if (ifcCheck && ClassUtils.isJavaLanguageInterface(baseType)) {
@@ -635,8 +636,15 @@ 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();
Annotation[] anns = getDeclaredAnnotations(ifcMethod);
if (anns.length == 0) {
return false;
}
@@ -649,21 +657,21 @@ 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;
/**
* Retrieve a potentially cached array of declared annotations for the
* given element.
* @param element the annotated element to introspect
* @return a potentially cached array of declared annotations
* (only for internal iteration purposes, not for external exposure)
* @since 5.1
*/
static Annotation[] getDeclaredAnnotations(AnnotatedElement element) {
if (element instanceof Class || element instanceof Member) {
// Class/Field/Method/Constructor returns a defensively cloned array from getDeclaredAnnotations.
// Since we use our result for internal iteration purposes only, it's safe to use a shared copy.
return declaredAnnotationsCache.computeIfAbsent(element, AnnotatedElement::getDeclaredAnnotations);
}
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;
return element.getDeclaredAnnotations();
}
/**
@@ -741,7 +749,7 @@ public abstract class AnnotationUtils {
if (annotation != null) {
return annotation;
}
for (Annotation declaredAnn : clazz.getDeclaredAnnotations()) {
for (Annotation declaredAnn : getDeclaredAnnotations(clazz)) {
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
annotation = findAnnotation(declaredType, annotationType, visited);
@@ -764,7 +772,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);
@@ -794,7 +802,7 @@ public abstract class AnnotationUtils {
*/
@Nullable
public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, @Nullable Class<?> clazz) {
if (clazz == null || Object.class == clazz) {
if (clazz == null || clazz == Object.class) {
return null;
}
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
@@ -828,8 +836,10 @@ public abstract class AnnotationUtils {
* @see #isAnnotationDeclaredLocally(Class, Class)
*/
@Nullable
public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
if (clazz == null || Object.class == clazz) {
public static Class<?> findAnnotationDeclaringClassForTypes(
List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
if (clazz == null || clazz == Object.class) {
return null;
}
for (Class<? extends Annotation> annotationType : annotationTypes) {
@@ -1965,6 +1975,7 @@ public abstract class AnnotationUtils {
public static void clearCache() {
findAnnotationCache.clear();
metaPresentCache.clear();
declaredAnnotationsCache.clear();
annotatedBaseTypeCache.clear();
synthesizableCache.clear();
attributeAliasesCache.clear();
@@ -2027,19 +2038,14 @@ public abstract class AnnotationUtils {
@Nullable
private final Class<? extends Annotation> containerAnnotationType;
private final boolean declaredMode;
private final Set<AnnotatedElement> visited = new HashSet<>();
private final Set<A> result = new LinkedHashSet<>();
AnnotationCollector(Class<A> annotationType,
@Nullable Class<? extends Annotation> containerAnnotationType, boolean declaredMode) {
AnnotationCollector(Class<A> annotationType,@Nullable Class<? extends Annotation> containerAnnotationType) {
this.annotationType = annotationType;
this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType :
resolveContainerAnnotationType(annotationType));
this.declaredMode = declaredMode;
}
Set<A> getResult(AnnotatedElement element) {
@@ -2051,7 +2057,7 @@ public abstract class AnnotationUtils {
private void process(AnnotatedElement element) {
if (this.visited.add(element)) {
try {
Annotation[] annotations = (this.declaredMode ? element.getDeclaredAnnotations() : element.getAnnotations());
Annotation[] annotations = getDeclaredAnnotations(element);
for (Annotation ann : annotations) {
Class<? extends Annotation> currentAnnotationType = ann.annotationType();
if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {