diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
index 382e3076274..d5568b2663c 100644
--- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
+++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
@@ -30,13 +30,59 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
- * Utility class used to collect and merge all annotation attributes on a
- * given {@link AnnotatedElement}, including those declared via meta-annotations.
+ * General utility methods for working with annotations and meta-annotations
+ * present on {@link AnnotatedElement AnnotatedElements}.
+ *
+ *
{@code AnnotatedElementUtils} defines the public API for Spring's
+ * meta-annotation programming model with support for annotation attribute
+ * overrides. If you do not need support for annotation attribute overrides,
+ * consider using {@link AnnotationUtils} instead.
+ *
+ *
Note that the features of this class are not provided by the JDK's
+ * introspection facilities themselves.
+ *
+ *
Annotation Attribute Override Support
+ * Support for meta-annotations with attribute overrides in
+ * composed annotations is provided by all
+ * {@code getAnnotationAttributes()} and {@code findAnnotationAttributes()}
+ * methods.
+ *
+ *
Find vs. Get Semantics
+ * The search algorithms used by methods in this class follow either
+ * find or get semantics. Consult the Javadoc for each
+ * individual method for details on which search algorithm is used.
+ *
+ *
Get semantics are limited to searching for annotations
+ * that are either present on an {@code AnnotatedElement} (i.e.,
+ * declared locally or {@linkplain java.lang.annotation.Inherited inherited})
+ * or declared within the annotation hierarchy above an {@code AnnotatedElement}.
+ *
+ *
Find semantics are much more exhaustive, providing
+ * get semantics plus support for the following:
+ *
+ *
+ * Searching on interfaces, if the annotated element is a class
+ * Searching on superclasses, if the annotated element is a class
+ * Resolving bridged methods, if the annotated element is a method
+ * Searching on methods in interfaces, if the annotated element is a method
+ * Searching on methods in superclasses, if the annotated element is a method
+ *
+ *
+ * Support for {@code @Inherited}
+ * Methods following get semantics will honor the contract of
+ * Java's {@link java.lang.annotation.Inherited @Inherited} annotation.
+ * However, methods following find semantics will ignore the
+ * presence of {@code @Inherited} since the find search algorithm
+ * manually traverses type and method hierarchies and thereby implicitly
+ * supports annotation inheritance without the need for {@code @Inherited}.
*
* @author Phillip Webb
* @author Juergen Hoeller
* @author Sam Brannen
* @since 4.0
+ * @see AnnotationUtils
+ * @see AnnotationAttributes
+ * @see BridgeMethodResolver
*/
public class AnnotatedElementUtils {
@@ -44,12 +90,12 @@ public class AnnotatedElementUtils {
/**
- * Get the fully qualified class names of all meta-annotation
+ * Get the fully qualified class names of all meta-annotation
* types present on the annotation (of the specified
* {@code annotationType}) on the supplied {@link AnnotatedElement}.
*
- *
This method finds all meta-annotations in the annotation hierarchy
- * above the specified annotation.
+ *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
*
* @param element the annotated element; never {@code null}
* @param annotationType the annotation type on which to find
@@ -66,12 +112,12 @@ public class AnnotatedElementUtils {
}
/**
- * Get the fully qualified class names of all meta-annotation
+ * Get the fully qualified class names of all meta-annotation
* types present on the annotation (of the specified
* {@code annotationType}) on the supplied {@link AnnotatedElement}.
*
- *
This method finds all meta-annotations in the annotation hierarchy
- * above the specified annotation.
+ *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
@@ -90,7 +136,7 @@ public class AnnotatedElementUtils {
try {
Annotation annotation = getAnnotation(element, annotationType);
if (annotation != null) {
- processWithGetSemantics(annotation.annotationType(), annotationType, new SimpleAnnotationProcessor() {
+ searchWithGetSemantics(annotation.annotationType(), annotationType, new SimpleAnnotationProcessor() {
@Override
public Object process(Annotation annotation, int metaDepth) {
@@ -112,8 +158,8 @@ public class AnnotatedElementUtils {
* a composed annotation that is meta-annotated with an
* annotation of the specified {@code annotationType}.
*
- * This method finds all meta-annotations in the annotation hierarchy
- * above the specified element.
+ *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the meta-annotation
@@ -125,7 +171,7 @@ public class AnnotatedElementUtils {
Assert.notNull(element, "AnnotatedElement must not be null");
Assert.hasText(annotationType, "annotationType must not be null or empty");
- return Boolean.TRUE.equals(processWithGetSemantics(element, annotationType, new SimpleAnnotationProcessor() {
+ return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, new SimpleAnnotationProcessor() {
@Override
public Boolean process(Annotation annotation, int metaDepth) {
boolean found = annotation.annotationType().getName().equals(annotationType);
@@ -142,6 +188,9 @@ public class AnnotatedElementUtils {
* If this method returns {@code true}, then {@link #getAnnotationAttributes}
* will return a non-null value.
*
+ *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
+ *
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
* type to find; never {@code null} or empty
@@ -151,7 +200,7 @@ public class AnnotatedElementUtils {
Assert.notNull(element, "AnnotatedElement must not be null");
Assert.hasText(annotationType, "annotationType must not be null or empty");
- return Boolean.TRUE.equals(processWithGetSemantics(element, annotationType, new SimpleAnnotationProcessor() {
+ return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, new SimpleAnnotationProcessor() {
@Override
public Boolean process(Annotation annotation, int metaDepth) {
boolean found = annotation.annotationType().getName().equals(annotationType);
@@ -161,13 +210,16 @@ public class AnnotatedElementUtils {
}
/**
- * Get annotation attributes of the specified {@code annotationType}
- * in the annotation hierarchy of the supplied {@link AnnotatedElement},
+ * Get annotation attributes of the specified {@code annotationType}
+ * in the annotation hierarchy of the supplied {@link AnnotatedElement}
* and merge the results into an {@link AnnotationAttributes} map.
*
* Delegates to {@link #getAnnotationAttributes(AnnotatedElement, String, boolean, boolean)},
* supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}.
*
+ *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
+ *
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
* type to find; never {@code null} or empty
@@ -180,10 +232,13 @@ public class AnnotatedElementUtils {
}
/**
- * Get annotation attributes of the specified {@code annotationType}
- * in the annotation hierarchy of the supplied {@link AnnotatedElement},
+ * Get annotation attributes of the specified {@code annotationType}
+ * in the annotation hierarchy of the supplied {@link AnnotatedElement}
* and merge the results into an {@link AnnotationAttributes} map.
*
+ *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
+ *
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
* type to find; never {@code null} or empty
@@ -197,25 +252,26 @@ public class AnnotatedElementUtils {
*/
public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationType,
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
- return processWithGetSemantics(element, annotationType, new MergedAnnotationAttributesProcessor(annotationType,
+ return searchWithGetSemantics(element, annotationType, new MergedAnnotationAttributesProcessor(annotationType,
classValuesAsString, nestedAnnotationsAsMap));
}
/**
- * Find annotation attributes of the specified {@code annotationType}
- * in the annotation hierarchy of the supplied {@link AnnotatedElement},
- * and merge the results into an {@link AnnotationAttributes} map.
+ * Find annotation attributes of the specified {@code annotationType}
+ * within annotation hierarchies above the supplied
+ * {@link AnnotatedElement} and merge the results into an
+ * {@link AnnotationAttributes} map.
*
- *
If the annotated element is a class, this algorithm will additionally
- * search on interfaces and superclasses.
- *
If the annotated element is a method, this algorithm will additionally
- * search on methods in interfaces and superclasses.
+ *
This method follows find semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
*
* @param element the annotated element; never {@code null}
* @param annotationType the annotation type to find; never {@code null}
* @return the merged {@code AnnotationAttributes}, or {@code null} if
* not found
* @since 4.2
+ * @see #findAnnotationAttributes(AnnotatedElement, String)
+ * @see #findAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element,
Class extends Annotation> annotationType) {
@@ -224,14 +280,13 @@ public class AnnotatedElementUtils {
}
/**
- * Find annotation attributes of the specified {@code annotationType}
- * in the annotation hierarchy of the supplied {@link AnnotatedElement},
- * and merge the results into an {@link AnnotationAttributes} map.
+ * Find annotation attributes of the specified {@code annotationType}
+ * within annotation hierarchies above the supplied
+ * {@link AnnotatedElement} and merge the results into an
+ * {@link AnnotationAttributes} map.
*
- *
If the annotated element is a class, this algorithm will additionally
- * search on interfaces and superclasses.
- *
If the annotated element is a method, this algorithm will additionally
- * search on methods in interfaces and superclasses.
+ *
This method follows find semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
@@ -239,20 +294,21 @@ public class AnnotatedElementUtils {
* @return the merged {@code AnnotationAttributes}, or {@code null} if
* not found
* @since 4.2
+ * @see #findAnnotationAttributes(AnnotatedElement, Class)
+ * @see #findAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element, String annotationType) {
return findAnnotationAttributes(element, annotationType, false, false);
}
/**
- * Find annotation attributes of the specified {@code annotationType}
- * in the annotation hierarchy of the supplied {@link AnnotatedElement},
- * and merge the results into an {@link AnnotationAttributes} map.
+ * Find annotation attributes of the specified {@code annotationType}
+ * within annotation hierarchies above the supplied
+ * {@link AnnotatedElement} and merge the results into an
+ * {@link AnnotationAttributes} map.
*
- *
If the annotated element is a class, this algorithm will additionally
- * search on interfaces and superclasses.
- *
If the annotated element is a method, this algorithm will additionally
- * search on methods in interfaces and superclasses.
+ *
This method follows find semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
@@ -265,6 +321,9 @@ public class AnnotatedElementUtils {
* @return the merged {@code AnnotationAttributes}, or {@code null} if
* not found
* @since 4.2
+ * @see #findAnnotationAttributes(AnnotatedElement, Class)
+ * @see #findAnnotationAttributes(AnnotatedElement, String)
+ * @see #getAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element, String annotationType,
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
@@ -273,9 +332,10 @@ public class AnnotatedElementUtils {
}
/**
- * Find annotation attributes of the specified {@code annotationType}
- * in the annotation hierarchy of the supplied {@link AnnotatedElement},
- * and merge the results into an {@link AnnotationAttributes} map.
+ * Find annotation attributes of the specified {@code annotationType}
+ * within annotation hierarchies above the supplied
+ * {@link AnnotatedElement} and merge the results into an
+ * {@link AnnotationAttributes} map.
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
@@ -296,24 +356,29 @@ public class AnnotatedElementUtils {
* @return the merged {@code AnnotationAttributes}, or {@code null} if
* not found
* @since 4.2
+ * @see #searchWithFindSemantics
+ * @see MergedAnnotationAttributesProcessor
*/
private static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element, String annotationType,
boolean searchOnInterfaces, boolean searchOnSuperclasses, boolean searchOnMethodsInInterfaces,
boolean searchOnMethodsInSuperclasses, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
- return processWithFindSemantics(element, annotationType, searchOnInterfaces, searchOnSuperclasses,
+ return searchWithFindSemantics(element, annotationType, searchOnInterfaces, searchOnSuperclasses,
searchOnMethodsInInterfaces, searchOnMethodsInSuperclasses, new MergedAnnotationAttributesProcessor(
annotationType, classValuesAsString, nestedAnnotationsAsMap));
}
/**
- * Get the annotation attributes of all annotations
+ * Get the annotation attributes of all annotations
* of the specified {@code annotationType} in the annotation hierarchy above
* the supplied {@link AnnotatedElement} and store the results in a
* {@link MultiValueMap}.
*
*
Note: in contrast to {@link #getAnnotationAttributes(AnnotatedElement, String)},
- * this method does not take attribute overrides into account.
+ * this method does not support attribute overrides.
+ *
+ *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
@@ -328,13 +393,16 @@ public class AnnotatedElementUtils {
}
/**
- * Get the annotation attributes of all annotations
+ * Get the annotation attributes of all annotations
* of the specified {@code annotationType} in the annotation hierarchy above
* the supplied {@link AnnotatedElement} and store the results in a
* {@link MultiValueMap}.
*
*
Note: in contrast to {@link #getAnnotationAttributes(AnnotatedElement, String)},
- * this method does not take attribute overrides into account.
+ * this method does not support attribute overrides.
+ *
+ *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level Javadoc}.
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
@@ -353,7 +421,7 @@ public class AnnotatedElementUtils {
final MultiValueMap attributesMap = new LinkedMultiValueMap();
- processWithGetSemantics(element, annotationType, new SimpleAnnotationProcessor() {
+ searchWithGetSemantics(element, annotationType, new SimpleAnnotationProcessor() {
@Override
public Void process(Annotation annotation, int metaDepth) {
@@ -375,8 +443,8 @@ public class AnnotatedElementUtils {
}
/**
- * Process all annotations of the specified {@code annotationType} and
- * recursively all meta-annotations on the specified {@code element}.
+ * Search for annotations of the specified {@code annotationType} on
+ * the specified {@code element}, following get semantics .
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
@@ -384,9 +452,9 @@ public class AnnotatedElementUtils {
* @param processor the processor to delegate to
* @return the result of the processor, potentially {@code null}
*/
- private static T processWithGetSemantics(AnnotatedElement element, String annotationType, Processor processor) {
+ private static T searchWithGetSemantics(AnnotatedElement element, String annotationType, Processor processor) {
try {
- return processWithGetSemantics(element, annotationType, processor, new HashSet(), 0);
+ return searchWithGetSemantics(element, annotationType, processor, new HashSet(), 0);
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
@@ -394,7 +462,7 @@ public class AnnotatedElementUtils {
}
/**
- * Perform the search algorithm for the {@link #processWithGetSemantics}
+ * Perform the search algorithm for the {@link #searchWithGetSemantics}
* method, avoiding endless recursion by tracking which annotated elements
* have already been visited .
*
@@ -410,7 +478,7 @@ public class AnnotatedElementUtils {
* @param metaDepth the meta-depth of the annotation
* @return the result of the processor, potentially {@code null}
*/
- private static T processWithGetSemantics(AnnotatedElement element, String annotationType,
+ private static T searchWithGetSemantics(AnnotatedElement element, String annotationType,
Processor processor, Set visited, int metaDepth) {
Assert.notNull(element, "AnnotatedElement must not be null");
@@ -435,7 +503,7 @@ public class AnnotatedElementUtils {
// Search in meta annotations on local annotations
for (Annotation annotation : annotations) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
- T result = processWithGetSemantics(annotation.annotationType(), annotationType, processor,
+ T result = searchWithGetSemantics(annotation.annotationType(), annotationType, processor,
visited, metaDepth + 1);
if (result != null) {
processor.postProcess(annotation, result);
@@ -453,8 +521,8 @@ public class AnnotatedElementUtils {
}
/**
- * Process all annotations of the specified {@code annotationType} and
- * recursively all meta-annotations on the specified {@code element}.
+ * Search for annotations of the specified {@code annotationType} on
+ * the specified {@code element}, following find semantics .
*
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
@@ -470,12 +538,12 @@ public class AnnotatedElementUtils {
* @param processor the processor to delegate to
* @return the result of the processor, potentially {@code null}
*/
- private static T processWithFindSemantics(AnnotatedElement element, String annotationType,
+ private static T searchWithFindSemantics(AnnotatedElement element, String annotationType,
boolean searchOnInterfaces, boolean searchOnSuperclasses, boolean searchOnMethodsInInterfaces,
boolean searchOnMethodsInSuperclasses, Processor processor) {
try {
- return processWithFindSemantics(element, annotationType, searchOnInterfaces, searchOnSuperclasses,
+ return searchWithFindSemantics(element, annotationType, searchOnInterfaces, searchOnSuperclasses,
searchOnMethodsInInterfaces, searchOnMethodsInSuperclasses, processor, new HashSet(), 0);
}
catch (Throwable ex) {
@@ -484,7 +552,7 @@ public class AnnotatedElementUtils {
}
/**
- * Perform the search algorithm for the {@link #processWithFindSemantics}
+ * Perform the search algorithm for the {@link #searchWithFindSemantics}
* method, avoiding endless recursion by tracking which annotated elements
* have already been visited .
*
@@ -508,7 +576,7 @@ public class AnnotatedElementUtils {
* @param metaDepth the meta-depth of the annotation
* @return the result of the processor, potentially {@code null}
*/
- private static T processWithFindSemantics(AnnotatedElement element, String annotationType,
+ private static T searchWithFindSemantics(AnnotatedElement element, String annotationType,
boolean searchOnInterfaces, boolean searchOnSuperclasses, boolean searchOnMethodsInInterfaces,
boolean searchOnMethodsInSuperclasses, Processor processor, Set visited, int metaDepth) {
@@ -536,7 +604,7 @@ public class AnnotatedElementUtils {
// Search in meta annotations on local annotations
for (Annotation annotation : annotations) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
- T result = processWithFindSemantics(annotation.annotationType(), annotationType,
+ T result = searchWithFindSemantics(annotation.annotationType(), annotationType,
searchOnInterfaces, searchOnSuperclasses, searchOnMethodsInInterfaces,
searchOnMethodsInSuperclasses, processor, visited, metaDepth + 1);
if (result != null) {
@@ -551,7 +619,7 @@ public class AnnotatedElementUtils {
// Search on possibly bridged method
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
- T result = processWithFindSemantics(resolvedMethod, annotationType, searchOnInterfaces,
+ T result = searchWithFindSemantics(resolvedMethod, annotationType, searchOnInterfaces,
searchOnSuperclasses, searchOnMethodsInInterfaces, searchOnMethodsInSuperclasses, processor,
visited, metaDepth);
if (result != null) {
@@ -582,7 +650,7 @@ public class AnnotatedElementUtils {
Method equivalentMethod = clazz.getDeclaredMethod(method.getName(),
method.getParameterTypes());
Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod);
- result = processWithFindSemantics(resolvedEquivalentMethod, annotationType,
+ result = searchWithFindSemantics(resolvedEquivalentMethod, annotationType,
searchOnInterfaces, searchOnSuperclasses, searchOnMethodsInInterfaces,
searchOnMethodsInSuperclasses, processor, visited, metaDepth);
if (result != null) {
@@ -612,7 +680,7 @@ public class AnnotatedElementUtils {
// Search on interfaces
if (searchOnInterfaces) {
for (Class> ifc : clazz.getInterfaces()) {
- T result = processWithFindSemantics(ifc, annotationType, searchOnInterfaces,
+ T result = searchWithFindSemantics(ifc, annotationType, searchOnInterfaces,
searchOnSuperclasses, searchOnMethodsInInterfaces, searchOnMethodsInSuperclasses,
processor, visited, metaDepth);
if (result != null) {
@@ -625,7 +693,7 @@ public class AnnotatedElementUtils {
if (searchOnSuperclasses) {
Class> superclass = clazz.getSuperclass();
if (superclass != null && !superclass.equals(Object.class)) {
- T result = processWithFindSemantics(superclass, annotationType, searchOnInterfaces,
+ T result = searchWithFindSemantics(superclass, annotationType, searchOnInterfaces,
searchOnSuperclasses, searchOnMethodsInInterfaces, searchOnMethodsInSuperclasses,
processor, visited, metaDepth);
if (result != null) {
@@ -650,7 +718,7 @@ public class AnnotatedElementUtils {
if (AnnotationUtils.isInterfaceWithAnnotatedMethods(iface)) {
try {
Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());
- T result = processWithFindSemantics(equivalentMethod, annotationType, searchOnInterfaces,
+ T result = searchWithFindSemantics(equivalentMethod, annotationType, searchOnInterfaces,
searchOnSuperclasses, searchOnMethodsInInterfaces, searchOnMethodsInSuperclasses, processor,
visited, metaDepth);