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 5c3fd570672..6acc5622215 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 @@ -55,7 +55,8 @@ import org.springframework.util.MultiValueMap; *

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}. + * or declared within the annotation hierarchy above an + * {@code AnnotatedElement}. * *

Find semantics are much more exhaustive, providing * get semantics plus support for the following: @@ -183,7 +184,7 @@ public class AnnotatedElementUtils { /** * Determine if an annotation of the specified {@code annotationType} * is present on the supplied {@link AnnotatedElement} or - * within the annotation hierarchy above the specified element. + * within the annotation hierarchy above the specified element. * *

If this method returns {@code true}, then {@link #getAnnotationAttributes} * will return a non-null value. @@ -210,16 +211,14 @@ public class AnnotatedElementUtils { } /** - * 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. + * Get the first annotation of the specified {@code annotationType} within + * the annotation hierarchy above the supplied {@code element} and + * merge that annotation's attributes with matching attributes from + * annotations in lower levels of the annotation hierarchy. * - *

Delegates to {@link #getAnnotationAttributes(AnnotatedElement, String, boolean, boolean)}, + *

This method 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 @@ -228,15 +227,26 @@ public class AnnotatedElementUtils { * @see #getAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #findAnnotationAttributes(AnnotatedElement, Class) * @see #findAnnotationAttributes(AnnotatedElement, String) + * @see #getAllAnnotationAttributes(AnnotatedElement, String) */ public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationType) { return getAnnotationAttributes(element, annotationType, false, false); } /** - * 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. + * Get the first annotation of the specified {@code annotationType} within + * the annotation hierarchy above the supplied {@code element} and + * merge that annotation's attributes with matching attributes from + * annotations in lower levels of the annotation hierarchy. + * + *

Attributes from lower levels in the annotation hierarchy override + * attributes of the same name from higher levels. + * + *

In contrast to {@link #getAllAnnotationAttributes}, the search + * algorithm used by this method will stop searching the annotation + * hierarchy once the first annotation of the specified + * {@code annotationType} has been found. As a consequence, additional + * annotations of the specified {@code annotationType} will be ignored. * *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level Javadoc}. @@ -247,13 +257,14 @@ public class AnnotatedElementUtils { * @param classValuesAsString whether to convert Class references into * Strings or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation - * instances into {@link AnnotationAttributes} maps or to preserve them + * instances into {@code AnnotationAttributes} maps or to preserve them * as Annotation instances * @return the merged {@code AnnotationAttributes}, or {@code null} if * not found * @see #findAnnotationAttributes(AnnotatedElement, Class) * @see #findAnnotationAttributes(AnnotatedElement, String) * @see #findAnnotationAttributes(AnnotatedElement, String, boolean, boolean) + * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { @@ -262,13 +273,13 @@ public class AnnotatedElementUtils { } /** - * 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. + * Find the first annotation of the specified {@code annotationType} within + * the annotation hierarchy above the supplied {@code element} and + * merge that annotation's attributes with matching attributes from + * annotations in lower levels of the annotation hierarchy. * - *

This method follows find semantics as described in the - * {@linkplain AnnotatedElementUtils class-level Javadoc}. + *

This method delegates to {@link #findAnnotationAttributes(AnnotatedElement, String, boolean, boolean)} + * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}. * * @param element the annotated element; never {@code null} * @param annotationType the annotation type to find; never {@code null} @@ -281,17 +292,17 @@ public class AnnotatedElementUtils { public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element, Class annotationType) { Assert.notNull(annotationType, "annotationType must not be null"); - return findAnnotationAttributes(element, annotationType.getName()); + return findAnnotationAttributes(element, annotationType.getName(), false, false); } /** - * 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. + * Find the first annotation of the specified {@code annotationType} within + * the annotation hierarchy above the supplied {@code element} and + * merge that annotation's attributes with matching attributes from + * annotations in lower levels of the annotation hierarchy. * - *

This method follows find semantics as described in the - * {@linkplain AnnotatedElementUtils class-level Javadoc}. + *

This method delegates to {@link #findAnnotationAttributes(AnnotatedElement, String, boolean, boolean)} + * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}. * * @param element the annotated element; never {@code null} * @param annotationType the fully qualified class name of the annotation @@ -307,10 +318,19 @@ public class AnnotatedElementUtils { } /** - * 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. + * Find the first annotation of the specified {@code annotationType} within + * the annotation hierarchy above the supplied {@code element} and + * merge that annotation's attributes with matching attributes from + * annotations in lower levels of the annotation hierarchy. + * + *

Attributes from lower levels in the annotation hierarchy override + * attributes of the same name from higher levels. + * + *

In contrast to {@link #getAllAnnotationAttributes}, the search + * algorithm used by this method will stop searching the annotation + * hierarchy once the first annotation of the specified + * {@code annotationType} has been found. As a consequence, additional + * annotations of the specified {@code annotationType} will be ignored. * *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level Javadoc}. @@ -321,7 +341,7 @@ public class AnnotatedElementUtils { * @param classValuesAsString whether to convert Class references into * Strings or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation - * instances into {@link AnnotationAttributes} maps or to preserve them + * instances into {@code AnnotationAttributes} maps or to preserve them * as Annotation instances * @return the merged {@code AnnotationAttributes}, or {@code null} if * not found @@ -337,10 +357,10 @@ public class AnnotatedElementUtils { } /** - * 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. + * Find the first annotation of the specified {@code annotationType} within + * the annotation hierarchy above the supplied {@code element} and + * merge that annotation's attributes with matching attributes from + * annotations in lower levels of the annotation hierarchy. * * @param element the annotated element; never {@code null} * @param annotationType the fully qualified class name of the annotation @@ -356,7 +376,7 @@ public class AnnotatedElementUtils { * @param classValuesAsString whether to convert Class references into * Strings or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation - * instances into {@link AnnotationAttributes} maps or to preserve them + * instances into {@code AnnotationAttributes} maps or to preserve them * as Annotation instances * @return the merged {@code AnnotationAttributes}, or {@code null} if * not found @@ -415,7 +435,7 @@ public class AnnotatedElementUtils { * @param classValuesAsString whether to convert Class references into * Strings or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation - * instances into {@link AnnotationAttributes} maps or to preserve them + * instances into {@code AnnotationAttributes} maps or to preserve them * as Annotation instances * @return a {@link MultiValueMap} keyed by attribute name, containing * the annotation attributes from all annotations found, or {@code null} 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 1a92f718e78..b7e21147c95 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 @@ -61,6 +61,12 @@ import org.springframework.util.StringUtils; * attribute overrides in composed annotations, use * {@link AnnotatedElementUtils} instead. * + *

Search Scope

+ *

The search algorithms used by methods in this class stop searching for + * an annotation once the first annotation of the specified type has been + * found. As a consequence, additional annotations of the specified type will + * be silently ignored. + * * @author Rob Harrop * @author Juergen Hoeller * @author Sam Brannen @@ -96,7 +102,7 @@ public abstract class AnnotationUtils { * {@code find*()} methods instead. * @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 not found + * @return the first matching annotation, or {@code null} if not found * @since 4.0 */ @SuppressWarnings("unchecked") @@ -123,7 +129,7 @@ public abstract class AnnotationUtils { * {@link #findAnnotation(AnnotatedElement, Class)} instead. * @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 not found + * @return the first matching annotation, or {@code null} if not found * @since 3.1 */ public static A getAnnotation(AnnotatedElement annotatedElement, Class annotationType) { @@ -156,7 +162,7 @@ public abstract class AnnotationUtils { * {@link #findAnnotation(Method, Class)} instead. * @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 + * @return the first matching annotation, or {@code null} if not found * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) * @see #getAnnotation(AnnotatedElement, Class) */ @@ -274,7 +280,7 @@ public abstract class AnnotationUtils { * instead. * @param annotatedElement the {@code AnnotatedElement} on which to find 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 not found + * @return the first matching annotation, or {@code null} if not found * @since 4.2 */ public static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) { @@ -290,7 +296,7 @@ public abstract class AnnotationUtils { * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @param visited the set of annotations that have already been visited - * @return the matching annotation, or {@code null} if not found + * @return the first matching annotation, or {@code null} if not found * @since 4.2 */ @SuppressWarnings("unchecked") @@ -330,7 +336,7 @@ public abstract class AnnotationUtils { * this explicitly. * @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 + * @return the first matching annotation, or {@code null} if not found * @see #getAnnotation(Method, Class) */ @SuppressWarnings("unchecked") @@ -434,7 +440,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 matching annotation, or {@code null} if not found + * @return the first matching annotation, or {@code null} if not found */ @SuppressWarnings("unchecked") public static A findAnnotation(Class clazz, Class annotationType) { @@ -456,7 +462,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 matching annotation, or {@code null} if not found + * @return the first matching annotation, or {@code null} if not found */ @SuppressWarnings("unchecked") private static A findAnnotation(Class clazz, Class annotationType, Set visited) {