Document search scope in Ann*[Element]Utils

This commit improves the documentation for AnnotationUtils and
AnnotatedElementUtils by explaining that the scope of most annotation
searches is limited to finding the first such annotation, resulting in
additional such annotations being silently ignored.

Issue: SPR-13015
This commit is contained in:
Sam Brannen 2015-05-12 21:06:27 +02:00
parent 00147379f9
commit 52153bd454
2 changed files with 71 additions and 45 deletions

View File

@ -55,7 +55,8 @@ import org.springframework.util.MultiValueMap;
* <p><strong>Get semantics</strong> are limited to searching for annotations
* that are either <em>present</em> 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 <em>above</em> an
* {@code AnnotatedElement}.
*
* <p><strong>Find semantics</strong> are much more exhaustive, providing
* <em>get semantics</em> plus support for the following:
@ -183,7 +184,7 @@ public class AnnotatedElementUtils {
/**
* Determine if an annotation of the specified {@code annotationType}
* is <em>present</em> on the supplied {@link AnnotatedElement} or
* within the annotation hierarchy above the specified element.
* within the annotation hierarchy <em>above</em> the specified element.
*
* <p>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 <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
*
* <p>Delegates to {@link #getAnnotationAttributes(AnnotatedElement, String, boolean, boolean)},
* <p>This method delegates to {@link #getAnnotationAttributes(AnnotatedElement, String, boolean, boolean)},
* supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}.
*
* <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 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 <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
*
* <p>Attributes from lower levels in the annotation hierarchy override
* attributes of the same name from higher levels.
*
* <p>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.
*
* <p>This method follows <em>get semantics</em> 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 <em>above</em> 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 <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
*
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level Javadoc}.
* <p>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 <em>above</em> 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 <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
*
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level Javadoc}.
* <p>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 <em>above</em> 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 <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
*
* <p>Attributes from lower levels in the annotation hierarchy override
* attributes of the same name from higher levels.
*
* <p>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.
*
* <p>This method follows <em>find semantics</em> 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 <em>above</em> 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 <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> 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}

View File

@ -61,6 +61,12 @@ import org.springframework.util.StringUtils;
* <em>attribute overrides</em> in <em>composed annotations</em>, use
* {@link AnnotatedElementUtils} instead.
*
* <h3>Search Scope</h3>
* <p>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 extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> 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 extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> 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 extends Annotation> A findAnnotation(Class<?> clazz, Class<A> 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 extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {