diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 6a427440ee..5ec94f43dd 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -38,10 +38,12 @@ import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** - * General utility methods for working with annotations, handling bridge methods - * (which the compiler generates for generic declarations) as well as super methods - * (for optional "annotation inheritance"). Note that none of this is - * provided by the JDK's introspection facilities themselves. + * General utility methods for working with annotations, handling meta-annotations, + * bridge methods (which the compiler generates for generic declarations) as well + * as super methods (for optional annotation inheritance). + * + *

Note that most of the features of this class are not provided by the + * JDK's introspection facilities themselves. * *

As a general rule for runtime-retained annotations (e.g. for transaction * control, authorization, or service exposure), always use the lookup methods @@ -52,6 +54,13 @@ import org.springframework.util.StringUtils; * ({@link #getAnnotation(Method, Class)}) and a find lookup in the entire * inheritance hierarchy of the given method ({@link #findAnnotation(Method, Class)}). * + *

Meta-annotation Support

+ *

Most {@code find*()} methods and some {@code get*()} methods in this class + * provide support for meta-annotations. Consult the Javadoc for each method in + * this class for details. For support for meta-annotations with + * attribute overrides in composed annotations, use + * {@link AnnotatedElementUtils} instead. + * * @author Rob Harrop * @author Juergen Hoeller * @author Sam Brannen @@ -59,8 +68,9 @@ import org.springframework.util.StringUtils; * @author Chris Beams * @author Phillip Webb * @since 2.0 - * @see java.lang.reflect.Method#getAnnotations() - * @see java.lang.reflect.Method#getAnnotation(Class) + * @see java.lang.reflect.AnnotatedElement#getAnnotations() + * @see java.lang.reflect.AnnotatedElement#getAnnotation(Class) + * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations() */ public abstract class AnnotationUtils { @@ -79,10 +89,13 @@ public abstract class AnnotationUtils { /** * Get a single {@link Annotation} of {@code annotationType} from the supplied - * annotation: either the given annotation itself or a meta-annotation thereof. + * annotation: either the given annotation itself or a direct meta-annotation + * thereof. + *

Note that this method does not support arbitrary levels of + * meta-annotations. * @param ann the Annotation to check * @param annotationType the annotation type to look for, both locally and as a meta-annotation - * @return the matching annotation, or {@code null} if none found + * @return the matching annotation, or {@code null} if not found * @since 4.0 */ @SuppressWarnings("unchecked") @@ -102,11 +115,12 @@ public abstract class AnnotationUtils { /** * Get a single {@link Annotation} of {@code annotationType} from the supplied - * Method, Constructor or Field. Meta-annotations will be searched if the annotation - * is not declared locally on the supplied element. - * @param annotatedElement the Method, Constructor or Field from which to get the annotation + * {@link AnnotatedElement}. + *

Meta-annotations will be searched if the annotation is not + * directly present on the supplied element. + * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation - * @return the matching annotation, or {@code null} if none found + * @return the matching annotation, or {@code null} if not found * @since 3.1 */ public static T getAnnotation(AnnotatedElement annotatedElement, Class annotationType) { @@ -130,10 +144,29 @@ public abstract class AnnotationUtils { } /** - * Get all {@link Annotation Annotations} from the supplied Method, Constructor or Field. + * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method}. + *

Correctly handles bridge {@link Method Methods} generated by the compiler. + *

Meta-annotations will be searched if the annotation is not + * directly present on the supplied method. + * @param method the method to look for annotations on + * @param annotationType the annotation type to look for + * @return the matching annotation, or {@code null} if not found + * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) + * @see #getAnnotation(AnnotatedElement, Class) + */ + public static A getAnnotation(Method method, Class annotationType) { + Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); + return getAnnotation((AnnotatedElement) resolvedMethod, annotationType); + } + + /** + * Get all {@link Annotation Annotations} that are present on the + * supplied {@link AnnotatedElement}. + *

Meta-annotations will not be searched. * @param annotatedElement the Method, Constructor or Field to retrieve annotations from - * @return the annotations found, or {@code null} if not resolvable (e.g. because nested - * Class values in annotation attributes failed to resolve at runtime) + * @return the annotations found, an empty array, or {@code null} if not + * resolvable (e.g. because nested Class values in annotation attributes + * failed to resolve at runtime) * @since 4.0.8 */ public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) { @@ -143,16 +176,21 @@ public abstract class AnnotationUtils { catch (Exception ex) { // Assuming nested Class values not resolvable within annotation attributes... logIntrospectionFailure(annotatedElement, ex); - return null; } + return null; } /** - * Get all {@link Annotation Annotations} from the supplied {@link Method}. + * Get all {@link Annotation Annotations} that are presentCorrectly handles bridge {@link Method Methods} generated by the compiler. + *

Meta-annotations will not be searched. * @param method the Method to retrieve annotations from - * @return the annotations found + * @return the annotations found, an empty array, or {@code null} if not + * resolvable (e.g. because nested Class values in annotation attributes + * failed to resolve at runtime) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) + * @see AnnotatedElement#getAnnotations() */ public static Annotation[] getAnnotations(Method method) { try { @@ -161,34 +199,25 @@ public abstract class AnnotationUtils { catch (Exception ex) { // Assuming nested Class values not resolvable within annotation attributes... logIntrospectionFailure(method, ex); - return null; } + return null; } /** - * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method}. - *

Correctly handles bridge {@link Method Methods} generated by the compiler. - * @param method the method to look for annotations on - * @param annotationType the annotation type to look for - * @return the annotations found - * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) - */ - public static A getAnnotation(Method method, Class annotationType) { - Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); - return getAnnotation((AnnotatedElement) resolvedMethod, annotationType); - } - - /** - * Get the possibly repeating {@link Annotation}s of {@code annotationType} from the - * supplied {@link Method}. Deals with both a single direct annotation and repeating - * annotations nested within a containing annotation. + * Get the possibly repeating {@link Annotation}s of {@code annotationType} + * from the supplied {@link Method}. + *

Deals with both a single direct annotation and repeating annotations + * nested within a containing annotation. *

Correctly handles bridge {@link Method Methods} generated by the compiler. + *

Meta-annotations will be searched if the annotation is not + * directly present on the supplied method. * @param method the method to look for annotations on * @param containerAnnotationType the class of the container that holds the annotations * @param annotationType the annotation type to look for - * @return the annotations found + * @return the annotations found or an empty set; never {@code null} * @since 4.0 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) + * @see java.lang.annotation.Repeatable */ public static Set getRepeatableAnnotation(Method method, Class containerAnnotationType, Class annotationType) { @@ -199,15 +228,17 @@ public abstract class AnnotationUtils { /** * Get the possibly repeating {@link Annotation}s of {@code annotationType} from the - * supplied {@link AnnotatedElement}. Deals with both a single direct annotation and - * repeating annotations nested within a containing annotation. - *

Correctly handles bridge {@link Method Methods} generated by the compiler. + * supplied {@link AnnotatedElement}. + *

Deals with both a single direct annotation and repeating annotations + * nested within a containing annotation. + *

Meta-annotations will be searched if the annotation is not + * directly present on the supplied element. * @param annotatedElement the element to look for annotations on * @param containerAnnotationType the class of the container that holds the annotations * @param annotationType the annotation type to look for - * @return the annotations found + * @return the annotations found or an empty set; never {@code null} * @since 4.0 - * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) + * @see java.lang.annotation.Repeatable */ public static Set getRepeatableAnnotation(AnnotatedElement annotatedElement, Class containerAnnotationType, Class annotationType) { @@ -230,9 +261,10 @@ public abstract class AnnotationUtils { * interfaces) if no annotation can be found on the given method itself. *

Annotations on methods are not inherited by default, so we need to handle * this explicitly. + *

Meta-annotations will not be searched. * @param method the method to look for annotations on * @param annotationType the annotation type to look for - * @return the annotation found, or {@code null} if none + * @return the matching annotation, or {@code null} if not found */ @SuppressWarnings("unchecked") public static A findAnnotation(Method method, Class annotationType) { @@ -328,7 +360,7 @@ public abstract class AnnotationUtils { * annotation, or superclass as the class to look for annotations on. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for - * @return the annotation if found, or {@code null} if not found + * @return the matching annotation, or {@code null} if not found */ @SuppressWarnings("unchecked") public static A findAnnotation(Class clazz, Class annotationType) { @@ -350,7 +382,7 @@ public abstract class AnnotationUtils { * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @param visited the set of annotations that have already been visited - * @return the annotation if found, or {@code null} if not found + * @return the matching annotation, or {@code null} if not found */ @SuppressWarnings("unchecked") private static A findAnnotation(Class clazz, Class annotationType, Set visited) { @@ -399,13 +431,14 @@ public abstract class AnnotationUtils { * {@code clazz} is {@code null}, {@code null} will be returned. *

If the supplied {@code clazz} is an interface, only the interface itself will be checked; * the inheritance hierarchy for interfaces will not be traversed. + *

Meta-annotations will not be searched. *

The standard {@link Class} API does not provide a mechanism for determining which class * in an inheritance hierarchy actually declares an {@link Annotation}, so we need to handle * this explicitly. * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @param clazz the class on which to check for the annotation (may be {@code null}) * @return the first {@link Class} in the inheritance hierarchy of the specified {@code clazz} - * which declares an annotation for the specified {@code annotationType}, or {@code null} + * which declares an annotation of the specified {@code annotationType}, or {@code null} * if not found * @see Class#isAnnotationPresent(Class) * @see Class#getDeclaredAnnotations() @@ -432,6 +465,7 @@ public abstract class AnnotationUtils { * returned. *

If the supplied {@code clazz} is an interface, only the interface itself * will be checked; the inheritance hierarchy for interfaces will not be traversed. + *

Meta-annotations will not be searched. *

The standard {@link Class} API does not provide a mechanism for determining * which class in an inheritance hierarchy actually declares one of several * candidate {@linkplain Annotation annotations}, so we need to handle this @@ -463,18 +497,20 @@ public abstract class AnnotationUtils { } /** - * Determine whether an annotation for the specified {@code annotationType} is - * declared locally on the supplied {@code clazz}. The supplied {@link Class} - * may represent any type. + * Determine whether an annotation of the specified {@code annotationType} is + * declared locally (i.e., directly present) on the supplied + * {@code clazz}. The supplied {@link Class} may represent any type. + *

Meta-annotations will not be searched. *

Note: This method does not determine if the annotation is * {@linkplain java.lang.annotation.Inherited inherited}. For greater clarity * regarding inherited annotations, consider using * {@link #isAnnotationInherited(Class, Class)} instead. * @param annotationType the Class object corresponding to the annotation type * @param clazz the Class object corresponding to the class on which to check for the annotation - * @return {@code true} if an annotation for the specified {@code annotationType} - * is declared locally on the supplied {@code clazz} - * @see Class#getDeclaredAnnotations() + * @return {@code true} if an annotation of the specified {@code annotationType} + * is directly present on the supplied {@code clazz} + * @see java.lang.Class#getDeclaredAnnotations() + * @see java.lang.Class#getDeclaredAnnotation(Class) * @see #isAnnotationInherited(Class, Class) */ public static boolean isAnnotationDeclaredLocally(Class annotationType, Class clazz) { @@ -497,16 +533,17 @@ public abstract class AnnotationUtils { } /** - * Determine whether an annotation for the specified {@code annotationType} is present + * Determine whether an annotation of the specified {@code annotationType} is present * on the supplied {@code clazz} and is {@linkplain java.lang.annotation.Inherited inherited} * (i.e., not declared locally for the class). + *

Meta-annotations will not be searched. *

If the supplied {@code clazz} is an interface, only the interface itself will be checked. * In accordance with standard meta-annotation semantics, the inheritance hierarchy for interfaces * will not be traversed. See the {@linkplain java.lang.annotation.Inherited Javadoc} for the * {@code @Inherited} meta-annotation for further details regarding annotation inheritance. * @param annotationType the Class object corresponding to the annotation type * @param clazz the Class object corresponding to the class on which to check for the annotation - * @return {@code true} if an annotation for the specified {@code annotationType} is present + * @return {@code true} if an annotation of the specified {@code annotationType} is present * on the supplied {@code clazz} and is inherited * @see Class#isAnnotationPresent(Class) * @see #isAnnotationDeclaredLocally(Class, Class) @@ -530,21 +567,25 @@ public abstract class AnnotationUtils { /** * Retrieve the given annotation's attributes as a {@link Map}, preserving all - * attribute types as-is. + * attribute types. + *

Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} + * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters + * set to {@code false}. *

Note: This method actually returns an {@link AnnotationAttributes} instance. * However, the {@code Map} signature has been preserved for binary compatibility. * @param annotation the annotation to retrieve the attributes for * @return the Map of annotation attributes, with attribute names as keys and - * corresponding attribute values as values + * corresponding attribute values as values; never {@code null} + * @see #getAnnotationAttributes(Annotation, boolean, boolean) */ public static Map getAnnotationAttributes(Annotation annotation) { return getAnnotationAttributes(annotation, false, false); } /** - * Retrieve the given annotation's attributes as a {@link Map}. Equivalent to - * calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} with - * the {@code nestedAnnotationsAsMap} parameter set to {@code false}. + * Retrieve the given annotation's attributes as a {@link Map}. + *

Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} + * with the {@code nestedAnnotationsAsMap} parameter set to {@code false}. *

Note: This method actually returns an {@link AnnotationAttributes} instance. * However, the {@code Map} signature has been preserved for binary compatibility. * @param annotation the annotation to retrieve the attributes for @@ -552,7 +593,8 @@ public abstract class AnnotationUtils { * compatibility with {@link org.springframework.core.type.AnnotationMetadata} * or to preserve them as Class references * @return the Map of annotation attributes, with attribute names as keys and - * corresponding attribute values as values + * corresponding attribute values as values; never {@code null} + * @see #getAnnotationAttributes(Annotation, boolean, boolean) */ public static Map getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) { return getAnnotationAttributes(annotation, classValuesAsString, false); @@ -564,7 +606,7 @@ public abstract class AnnotationUtils { *

This method provides fully recursive annotation reading capabilities on par with * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. * @param annotation the annotation to retrieve the attributes for - * @param classValuesAsString whether to turn Class references into Strings (for + * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata} * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into @@ -572,7 +614,7 @@ public abstract class AnnotationUtils { * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as * Annotation instances * @return the annotation attributes (a specialized Map) with attribute names as keys - * and corresponding attribute values as values + * and corresponding attribute values as values; never {@code null} * @since 3.1.1 */ public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString, @@ -813,14 +855,15 @@ public abstract class AnnotationUtils { if (this.visited.add(element)) { try { for (Annotation ann : element.getAnnotations()) { - if (ObjectUtils.nullSafeEquals(this.annotationType, ann.annotationType())) { + Class currentAnnotationType = ann.annotationType(); + if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) { this.result.add((A) ann); } - else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, ann.annotationType())) { + else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) { this.result.addAll(getValue(ann)); } else if (!isInJavaLangAnnotationPackage(ann)) { - process(ann.annotationType()); + process(currentAnnotationType); } } }