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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user