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 extends Annotation> 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. * + *
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