From e27df06f919a1f1ef53b0571e1a15dfc9e2f707f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 5 Nov 2015 12:29:29 +0100 Subject: [PATCH] AnnotatedElementUtils consistently operates on actual annotation type if available (for performance reasons) The goal is to avoid String name comparisons in favor of annotation type identity checks wherever possible. Also, we avoid double getDeclaredAnnotations/getAnnotations checks on anything other than Classes now, since we'd just get the same result in a fresh array. Issue: SPR-13621 --- .../annotation/AnnotatedElementUtils.java | 473 +++++++++++------- 1 file changed, 294 insertions(+), 179 deletions(-) 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 a1944f75ec..0f9749747d 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 @@ -53,7 +53,7 @@ import org.springframework.util.MultiValueMap; * *

Find vs. Get Semantics

*

The search algorithms used by methods in this class follow either - * find or get semantics. Consult the Javadoc for each + * find or get semantics. Consult the javadocs for each * individual method for details on which search algorithm is used. * *

Get semantics are limited to searching for annotations @@ -98,14 +98,13 @@ public class AnnotatedElementUtils { /** - * 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}. + * 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 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 - * meta-annotations; never {@code null} + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationType the annotation type on which to find meta-annotations * @return the names of all meta-annotations present on the annotation, * or {@code null} if not found * @since 4.2 @@ -113,8 +112,28 @@ public class AnnotatedElementUtils { * @see #hasMetaAnnotationTypes */ public static Set getMetaAnnotationTypes(AnnotatedElement element, Class annotationType) { + Assert.notNull(element, "AnnotatedElement must not be null"); Assert.notNull(annotationType, "annotationType must not be null"); - return getMetaAnnotationTypes(element, annotationType.getName()); + final Set types = new LinkedHashSet(); + + try { + Annotation annotation = element.getAnnotation(annotationType); + if (annotation != null) { + searchWithGetSemantics(annotation.annotationType(), annotationType, null, new SimpleAnnotationProcessor() { + @Override + public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { + types.add(annotation.annotationType().getName()); + return CONTINUE; + } + }, new HashSet(), 1); + } + } + catch (Throwable ex) { + AnnotationUtils.rethrowAnnotationConfigurationException(ex); + throw new IllegalStateException("Failed to introspect annotations on " + element, ex); + } + + return (!types.isEmpty() ? types : null); } /** @@ -122,10 +141,10 @@ public class AnnotatedElementUtils { * types present on the annotation (of the specified * {@code annotationName}) on the supplied {@link AnnotatedElement}. *

This method follows get semantics as described in the - * {@linkplain AnnotatedElementUtils class-level Javadoc}. - * @param element the annotated element; never {@code null} + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element * @param annotationName the fully qualified class name of the annotation - * type on which to find meta-annotations; never {@code null} or empty + * type on which to find meta-annotations * @return the names of all meta-annotations present on the annotation, * or {@code null} if not found * @see #getMetaAnnotationTypes(AnnotatedElement, Class) @@ -134,14 +153,12 @@ public class AnnotatedElementUtils { public static Set getMetaAnnotationTypes(AnnotatedElement element, String annotationName) { Assert.notNull(element, "AnnotatedElement must not be null"); Assert.hasLength(annotationName, "annotationName must not be null or empty"); - final Set types = new LinkedHashSet(); try { Annotation annotation = AnnotationUtils.getAnnotation(element, annotationName); if (annotation != null) { - searchWithGetSemantics(annotation.annotationType(), annotationName, new SimpleAnnotationProcessor() { - + searchWithGetSemantics(annotation.annotationType(), null, annotationName, new SimpleAnnotationProcessor() { @Override public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { types.add(annotation.annotationType().getName()); @@ -163,10 +180,35 @@ public class AnnotatedElementUtils { * a composed annotation that is meta-annotated with an * annotation of the specified {@code annotationName}. *

This method follows get semantics as described in the - * {@linkplain AnnotatedElementUtils class-level Javadoc}. - * @param element the annotated element; never {@code null} - * @param annotationName the fully qualified class name of the meta-annotation - * type to find; never {@code null} or empty + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationType the annotation type on which to find meta-annotations + * @return {@code true} if a matching meta-annotation is present + * @since 4.2.3 + * @see #getMetaAnnotationTypes + */ + public static boolean hasMetaAnnotationTypes(AnnotatedElement element, final Class annotationType) { + Assert.notNull(element, "AnnotatedElement must not be null"); + Assert.notNull(annotationType, "annotationType must not be null"); + + return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, new SimpleAnnotationProcessor() { + @Override + public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { + boolean found = (annotation.annotationType() == annotationType); + return (found && metaDepth > 0 ? Boolean.TRUE : CONTINUE); + } + })); + } + + /** + * Determine if the supplied {@link AnnotatedElement} is annotated with a + * composed annotation that is meta-annotated with an annotation + * of the specified {@code annotationName}. + *

This method follows get semantics as described in the + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationName the fully qualified class name of the + * meta-annotation type to find * @return {@code true} if a matching meta-annotation is present * @see #getMetaAnnotationTypes */ @@ -174,7 +216,7 @@ public class AnnotatedElementUtils { Assert.notNull(element, "AnnotatedElement must not be null"); Assert.hasLength(annotationName, "annotationName must not be null or empty"); - return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationName, new SimpleAnnotationProcessor() { + return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor() { @Override public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { boolean found = annotation.annotationType().getName().equals(annotationName); @@ -190,21 +232,42 @@ public class AnnotatedElementUtils { *

If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} * 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 annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationType the annotation type on which to find meta-annotations + * @return {@code true} if a matching annotation is present + * @since 4.2.3 + */ + public static boolean isAnnotated(AnnotatedElement element, final Class annotationType) { + Assert.notNull(element, "AnnotatedElement must not be null"); + Assert.notNull(annotationType, "annotationType must not be null"); + + return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, new SimpleAnnotationProcessor() { + @Override + public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { + boolean found = annotation.annotationType() == annotationType; + return (found ? Boolean.TRUE : CONTINUE); + } + })); + } + + /** + * Determine if an annotation of the specified {@code annotationName} is + * present on the supplied {@link AnnotatedElement} or within the + * annotation hierarchy above the specified element. + *

If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} + * 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 + * @param annotationName the fully qualified class name of the annotation type to find * @return {@code true} if a matching annotation is present */ public static boolean isAnnotated(AnnotatedElement element, final String annotationName) { Assert.notNull(element, "AnnotatedElement must not be null"); Assert.hasLength(annotationName, "annotationName must not be null or empty"); - if (element.getAnnotations().length == 0) { - return false; - } - return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationName, new SimpleAnnotationProcessor() { + return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor() { @Override public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { boolean found = annotation.annotationType().getName().equals(annotationName); @@ -223,8 +286,8 @@ public class AnnotatedElementUtils { * within a single annotation and within the annotation hierarchy. *

This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, Class)} * and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}. - * @param element the annotated element; never {@code null} - * @param annotationType the annotation type to find; never {@code null} + * @param element the annotated element + * @param annotationType the annotation type to find * @return the merged, synthesized {@code Annotation}, or {@code null} if not found * @since 4.2 * @see #getMergedAnnotationAttributes(AnnotatedElement, Class) @@ -233,7 +296,7 @@ public class AnnotatedElementUtils { */ public static A getMergedAnnotation(AnnotatedElement element, Class annotationType) { AnnotationAttributes attributes = getMergedAnnotationAttributes(element, annotationType); - return ((attributes != null) ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null); + return (attributes != null ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null); } /** @@ -244,8 +307,8 @@ public class AnnotatedElementUtils { *

{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. *

This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}. - * @param element the annotated element; never {@code null} - * @param annotationType the annotation type to find; never {@code null} + * @param element the annotated element + * @param annotationType the annotation type to find * @return the merged {@code AnnotationAttributes}, or {@code null} if not found * @since 4.2 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) @@ -253,17 +316,14 @@ public class AnnotatedElementUtils { * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #findMergedAnnotation(AnnotatedElement, Class) */ - public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, Class annotationType) { - Assert.notNull(annotationType, "annotationType must not be null"); - return getMergedAnnotationAttributes(element, annotationType.getName()); - } + public static AnnotationAttributes getMergedAnnotationAttributes( + AnnotatedElement element, Class annotationType) { - /** - * @deprecated As of Spring Framework 4.2, use {@link #getMergedAnnotationAttributes(AnnotatedElement, String)} instead. - */ - @Deprecated - public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationName) { - return getMergedAnnotationAttributes(element, annotationName); + Assert.notNull(annotationType, "annotationType must not be null"); + AnnotationAttributes attributes = searchWithGetSemantics(element, annotationType, null, + new MergedAnnotationAttributesProcessor(annotationType, null, false, false)); + AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false); + return attributes; } /** @@ -275,11 +335,9 @@ public class AnnotatedElementUtils { * within a single annotation and within the annotation hierarchy. *

This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)}, * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}. - * @param element the annotated element; never {@code null} - * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty - * @return the merged {@code AnnotationAttributes}, or {@code null} if - * not found + * @param element the annotated element + * @param annotationName the fully qualified class name of the annotation type to find + * @return the merged {@code AnnotationAttributes}, or {@code null} if not found * @since 4.2 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) @@ -290,55 +348,37 @@ public class AnnotatedElementUtils { return getMergedAnnotationAttributes(element, annotationName, false, false); } - /** - * @deprecated As of Spring Framework 4.2, use {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)} instead. - */ - @Deprecated - public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationName, - boolean classValuesAsString, boolean nestedAnnotationsAsMap) { - - return getMergedAnnotationAttributes(element, annotationName, classValuesAsString, nestedAnnotationsAsMap); - } - /** * Get the first annotation of the specified {@code annotationName} 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, and - * {@link AliasFor @AliasFor} semantics are fully supported, both - * within a single annotation and within the annotation hierarchy. - *

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 annotationName} has been found. As a consequence, additional - * annotations of the specified {@code annotationName} will be ignored. + *

Attributes from lower levels in the annotation hierarchy override attributes + * of the same name from higher levels, and {@link AliasFor @AliasFor} semantics are + * fully supported, both within a single annotation and within the annotation hierarchy. + *

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 annotationName} has been found. As a consequence, + * additional annotations of the specified {@code annotationName} will be ignored. *

This method follows get semantics as described in the - * {@linkplain AnnotatedElementUtils class-level Javadoc}. - * @param element the annotated element; never {@code null} - * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty - * @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 {@code AnnotationAttributes} maps or to preserve them - * as Annotation instances - * @return the merged {@code AnnotationAttributes}, or {@code null} if - * not found + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationName the fully qualified class name of the annotation type to find + * @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 {@code AnnotationAttributes} maps or to preserve them as Annotation instances + * @return the merged {@code AnnotationAttributes}, or {@code null} if not found * @since 4.2 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ - public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName, - boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, + String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { - if (element.getAnnotations().length == 0) { - return null; - } - AnnotationAttributes attributes = searchWithGetSemantics(element, annotationName, - new MergedAnnotationAttributesProcessor(annotationName, classValuesAsString, nestedAnnotationsAsMap)); + AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName, + new MergedAnnotationAttributesProcessor(null, annotationName, classValuesAsString, nestedAnnotationsAsMap)); AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); return attributes; } @@ -352,8 +392,8 @@ public class AnnotatedElementUtils { *

{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. *

This method delegates to {@link #findMergedAnnotation(AnnotatedElement, String)}. - * @param element the annotated element; never {@code null} - * @param annotationType the annotation type to find; never {@code null} + * @param element the annotated element + * @param annotationType the annotation type to find * @return the merged, synthesized {@code Annotation}, or {@code null} if not found * @since 4.2 * @see #findMergedAnnotation(AnnotatedElement, String) @@ -376,9 +416,8 @@ public class AnnotatedElementUtils { *

This method delegates to {@link #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)} * (supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}) * and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}. - * @param element the annotated element; never {@code null} - * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty + * @param element the annotated element + * @param annotationName the fully qualified class name of the annotation type to find * @return the merged, synthesized {@code Annotation}, or {@code null} if not found * @since 4.2 * @see #findMergedAnnotation(AnnotatedElement, Class) @@ -407,10 +446,9 @@ public class AnnotatedElementUtils { * {@code annotationName} has been found. As a consequence, additional * annotations of the specified {@code annotationName} will be ignored. *

This method follows find semantics as described in the - * {@linkplain AnnotatedElementUtils class-level Javadoc}. - * @param element the annotated element; never {@code null} - * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationType the annotation type to find * @param classValuesAsString whether to convert Class references into * Strings or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation @@ -421,31 +459,84 @@ public class AnnotatedElementUtils { * @since 4.2 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) + * @deprecated as of 4.2.3; use {@link #findMergedAnnotation(AnnotatedElement, Class)} instead */ - public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, String annotationName, - boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + @Deprecated + public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, + Class annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { - AnnotationAttributes attributes = searchWithFindSemantics(element, annotationName, - new MergedAnnotationAttributesProcessor(annotationName, classValuesAsString, nestedAnnotationsAsMap)); + AnnotationAttributes attributes = searchWithFindSemantics(element, annotationType, null, + new MergedAnnotationAttributesProcessor(annotationType, null, classValuesAsString, nestedAnnotationsAsMap)); AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); return attributes; } /** - * Get the annotation attributes of all annotations of - * the specified {@code annotationName} in the annotation hierarchy above - * the supplied {@link AnnotatedElement} and store the results in a - * {@link MultiValueMap}. + * Find the first annotation of the specified {@code annotationName} 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, and + * {@link AliasFor @AliasFor} semantics are fully supported, both + * within a single annotation and within the annotation hierarchy. + *

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 annotationName} has been found. As a consequence, additional + * annotations of the specified {@code annotationName} will be ignored. + *

This method follows find semantics as described in the + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationName the fully qualified class name of the annotation type to find + * @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 + * {@code AnnotationAttributes} maps or to preserve them as Annotation instances + * @return the merged {@code AnnotationAttributes}, or {@code null} if not found + * @since 4.2 + * @see #findMergedAnnotation(AnnotatedElement, Class) + * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) + */ + public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, + String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + + AnnotationAttributes attributes = searchWithFindSemantics(element, null, annotationName, + new MergedAnnotationAttributesProcessor(null, annotationName, classValuesAsString, nestedAnnotationsAsMap)); + AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); + return attributes; + } + + /** + * @deprecated As of Spring Framework 4.2, use {@link #getMergedAnnotationAttributes(AnnotatedElement, String)} instead. + */ + @Deprecated + public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationName) { + return getMergedAnnotationAttributes(element, annotationName); + } + + /** + * @deprecated As of Spring Framework 4.2, use {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)} instead. + */ + @Deprecated + public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationName, + boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + + return getMergedAnnotationAttributes(element, annotationName, classValuesAsString, nestedAnnotationsAsMap); + } + + /** + * Get the annotation attributes of all annotations of the specified + * {@code annotationName} in the annotation hierarchy above the supplied + * {@link AnnotatedElement} and store the results in a {@link MultiValueMap}. *

Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, * 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 annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty - * @return a {@link MultiValueMap} keyed by attribute name, containing - * the annotation attributes from all annotations found, or {@code null} - * if not found + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationName the fully qualified class name of the annotation type to find + * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation + * attributes from all annotations found, or {@code null} if not found * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ public static MultiValueMap getAllAnnotationAttributes(AnnotatedElement element, String annotationName) { @@ -460,31 +551,28 @@ public class AnnotatedElementUtils { *

Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, * 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 annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty - * @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 {@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} - * if not found + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element + * @param annotationName the fully qualified class name of the annotation type to find + * @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 + * {@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} if not found */ public static MultiValueMap getAllAnnotationAttributes(AnnotatedElement element, final String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { final MultiValueMap attributesMap = new LinkedMultiValueMap(); - searchWithGetSemantics(element, annotationName, new SimpleAnnotationProcessor() { + searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor() { @Override public Void process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { boolean found = annotation.annotationType().getName().equals(annotationName); if (found) { - AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation, - classValuesAsString, nestedAnnotationsAsMap); + AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes( + annotation, classValuesAsString, nestedAnnotationsAsMap); for (Map.Entry entry : annotationAttributes.entrySet()) { attributesMap.add(entry.getKey(), entry.getValue()); } @@ -500,15 +588,19 @@ public class AnnotatedElementUtils { /** * Search for annotations of the specified {@code annotationName} on * the specified {@code element}, following get semantics. - * @param element the annotated element; never {@code null} + * @param element the annotated element + * @param annotationType the annotation type on which to find meta-annotations * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty + * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to * @return the result of the processor, potentially {@code null} */ - private static T searchWithGetSemantics(AnnotatedElement element, String annotationName, Processor processor) { + private static T searchWithGetSemantics(AnnotatedElement element, + Class annotationType, String annotationName, Processor processor) { + try { - return searchWithGetSemantics(element, annotationName, processor, new HashSet(), 0); + return searchWithGetSemantics( + element, annotationType, annotationName, processor, new HashSet(), 0); } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); @@ -522,42 +614,45 @@ public class AnnotatedElementUtils { * have already been visited. *

The {@code metaDepth} parameter is explained in the * {@link Processor#process process()} method of the {@link Processor} API. - * @param element the annotated element; never {@code null} + * @param element the annotated element + * @param annotationType the annotation type on which to find meta-annotations * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty + * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation * @return the result of the processor, potentially {@code null} */ - private static T searchWithGetSemantics(AnnotatedElement element, String annotationName, + private static T searchWithGetSemantics(AnnotatedElement element, + Class annotationType, String annotationName, Processor processor, Set visited, int metaDepth) { Assert.notNull(element, "AnnotatedElement must not be null"); - Assert.hasLength(annotationName, "annotationName must not be null or empty"); if (visited.add(element)) { try { // Start searching within locally declared annotations List declaredAnnotations = Arrays.asList(element.getDeclaredAnnotations()); - T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations, annotationName, processor, - visited, metaDepth); + T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations, + annotationType, annotationName, processor, visited, metaDepth); if (result != null) { return result; } - List inheritedAnnotations = new ArrayList(); - for (Annotation annotation : element.getAnnotations()) { - if (!declaredAnnotations.contains(annotation)) { - inheritedAnnotations.add(annotation); + if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new + List inheritedAnnotations = new ArrayList(); + for (Annotation annotation : element.getAnnotations()) { + if (!declaredAnnotations.contains(annotation)) { + inheritedAnnotations.add(annotation); + } } - } - // Continue searching within inherited annotations - result = searchWithGetSemanticsInAnnotations( - element, inheritedAnnotations, annotationName, processor, visited, metaDepth); - if (result != null) { - return result; + // Continue searching within inherited annotations + result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, + annotationType, annotationName, processor, visited, metaDepth); + if (result != null) { + return result; + } } } catch (Exception ex) { @@ -570,7 +665,7 @@ public class AnnotatedElementUtils { /** * This method is invoked by - * {@link #searchWithGetSemantics(AnnotatedElement, String, Processor, Set, int)} + * {@link #searchWithGetSemantics(AnnotatedElement, Class, String, Processor, Set, int)} * to perform the actual search within the supplied list of annotations. *

This method should be invoked first with locally declared annotations * and then subsequently with inherited annotations, thereby allowing @@ -579,22 +674,26 @@ public class AnnotatedElementUtils { * {@link Processor#process process()} method of the {@link Processor} API. * @param annotatedElement the element that is annotated with the supplied * annotations, used for contextual logging; may be {@code null} if unknown - * @param annotations the annotations to search in; never {@code null} + * @param annotations the annotations to search in + * @param annotationType the annotation type on which to find meta-annotations * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty + * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation * @return the result of the processor, potentially {@code null} * @since 4.2 */ - private static T searchWithGetSemanticsInAnnotations(AnnotatedElement annotatedElement, List annotations, - String annotationName, Processor processor, Set visited, int metaDepth) { + private static T searchWithGetSemanticsInAnnotations(AnnotatedElement annotatedElement, + List annotations, Class annotationType, String annotationName, + Processor processor, Set visited, int metaDepth) { // Search in annotations for (Annotation annotation : annotations) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) && - (annotation.annotationType().getName().equals(annotationName) || metaDepth > 0)) { + ((annotationType != null ? annotation.annotationType() == annotationType : + annotation.annotationType().getName().equals(annotationName)) || + metaDepth > 0)) { T result = processor.process(annotatedElement, annotation, metaDepth); if (result != null) { return result; @@ -605,8 +704,8 @@ public class AnnotatedElementUtils { // Recursively search in meta-annotations for (Annotation annotation : annotations) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) { - T result = searchWithGetSemantics( - annotation.annotationType(), annotationName, processor, visited, metaDepth + 1); + T result = searchWithGetSemantics(annotation.annotationType(), annotationType, + annotationName, processor, visited, metaDepth + 1); if (result != null) { processor.postProcess(annotatedElement, annotation, result); return result; @@ -620,16 +719,20 @@ public class AnnotatedElementUtils { /** * Search for annotations of the specified {@code annotationName} on * the specified {@code element}, following find semantics. - * @param element the annotated element; never {@code null} + * @param element the annotated element + * @param annotationType the annotation type on which to find meta-annotations * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty + * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to * @return the result of the processor, potentially {@code null} * @since 4.2 */ - private static T searchWithFindSemantics(AnnotatedElement element, String annotationName, Processor processor) { + private static T searchWithFindSemantics( + AnnotatedElement element, Class annotationType, String annotationName, Processor processor) { + try { - return searchWithFindSemantics(element, annotationName, processor, new HashSet(), 0); + return searchWithFindSemantics( + element, annotationType, annotationName, processor, new HashSet(), 0); } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); @@ -643,17 +746,18 @@ public class AnnotatedElementUtils { * have already been visited. *

The {@code metaDepth} parameter is explained in the * {@link Processor#process process()} method of the {@link Processor} API. - * @param element the annotated element; never {@code null} + * @param element the annotated element + * @param annotationType the annotation type on which to find meta-annotations * @param annotationName the fully qualified class name of the annotation - * type to find; never {@code null} or empty + * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation * @return the result of the processor, potentially {@code null} * @since 4.2 */ - private static T searchWithFindSemantics(AnnotatedElement element, String annotationName, - Processor processor, Set visited, int metaDepth) { + private static T searchWithFindSemantics(AnnotatedElement element, Class annotationType, + String annotationName, Processor processor, Set visited, int metaDepth) { Assert.notNull(element, "AnnotatedElement must not be null"); Assert.hasLength(annotationName, "annotationName must not be null or empty"); @@ -665,8 +769,10 @@ public class AnnotatedElementUtils { // Search in local annotations for (Annotation annotation : annotations) { - if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) - && (annotation.annotationType().getName().equals(annotationName) || metaDepth > 0)) { + if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) && + ((annotationType != null ? annotation.annotationType() == annotationType : + annotation.annotationType().getName().equals(annotationName)) || + metaDepth > 0)) { T result = processor.process(element, annotation, metaDepth); if (result != null) { return result; @@ -677,8 +783,8 @@ public class AnnotatedElementUtils { // Search in meta annotations on local annotations for (Annotation annotation : annotations) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) { - T result = searchWithFindSemantics(annotation.annotationType(), annotationName, processor, - visited, metaDepth + 1); + T result = searchWithFindSemantics( + annotation.annotationType(), annotationType, annotationName, processor, visited, metaDepth + 1); if (result != null) { processor.postProcess(annotation.annotationType(), annotation, result); return result; @@ -691,14 +797,16 @@ public class AnnotatedElementUtils { // Search on possibly bridged method Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); - T result = searchWithFindSemantics(resolvedMethod, annotationName, processor, visited, metaDepth); + T result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName, + processor, visited, metaDepth); if (result != null) { return result; } // Search on methods in interfaces declared locally Class[] ifcs = method.getDeclaringClass().getInterfaces(); - result = searchOnInterfaces(method, annotationName, processor, visited, metaDepth, ifcs); + result = searchOnInterfaces( + method, annotationType, annotationName, processor, visited, metaDepth, ifcs); if (result != null) { return result; } @@ -714,8 +822,8 @@ public class AnnotatedElementUtils { try { Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod); - result = searchWithFindSemantics( - resolvedEquivalentMethod, annotationName, processor, visited, metaDepth); + result = searchWithFindSemantics(resolvedEquivalentMethod, annotationType, annotationName, + processor, visited, metaDepth); if (result != null) { return result; } @@ -725,8 +833,8 @@ public class AnnotatedElementUtils { } // Search on interfaces declared on superclass - result = searchOnInterfaces(method, annotationName, processor, visited, metaDepth, - clazz.getInterfaces()); + result = searchOnInterfaces(method, annotationType, annotationName, processor, visited, + metaDepth, clazz.getInterfaces()); if (result != null) { return result; } @@ -738,7 +846,8 @@ public class AnnotatedElementUtils { // Search on interfaces for (Class ifc : clazz.getInterfaces()) { - T result = searchWithFindSemantics(ifc, annotationName, processor, visited, metaDepth); + T result = searchWithFindSemantics( + ifc, annotationType, annotationName, processor, visited, metaDepth); if (result != null) { return result; } @@ -747,7 +856,8 @@ public class AnnotatedElementUtils { // Search on superclass Class superclass = clazz.getSuperclass(); if (superclass != null && Object.class != superclass) { - T result = searchWithFindSemantics(superclass, annotationName, processor, visited, metaDepth); + T result = searchWithFindSemantics( + superclass, annotationType, annotationName, processor, visited, metaDepth); if (result != null) { return result; } @@ -761,14 +871,15 @@ public class AnnotatedElementUtils { return null; } - private static T searchOnInterfaces(Method method, String annotationName, Processor processor, - Set visited, int metaDepth, Class[] ifcs) { + private static T searchOnInterfaces(Method method, Class annotationType, String annotationName, + Processor processor, Set visited, int metaDepth, Class[] ifcs) { for (Class iface : ifcs) { if (AnnotationUtils.isInterfaceWithAnnotatedMethods(iface)) { try { Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes()); - T result = searchWithFindSemantics(equivalentMethod, annotationName, processor, visited, metaDepth); + T result = searchWithFindSemantics(equivalentMethod, annotationType, annotationName, + processor, visited, metaDepth); if (result != null) { return result; } @@ -871,15 +982,18 @@ public class AnnotatedElementUtils { */ private static class MergedAnnotationAttributesProcessor implements Processor { + private final Class annotationType; + private final String annotationName; private final boolean classValuesAsString; private final boolean nestedAnnotationsAsMap; - MergedAnnotationAttributesProcessor(String annotationName, boolean classValuesAsString, - boolean nestedAnnotationsAsMap) { + MergedAnnotationAttributesProcessor(Class annotationType, String annotationName, + boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + this.annotationType = annotationType; this.annotationName = annotationName; this.classValuesAsString = classValuesAsString; this.nestedAnnotationsAsMap = nestedAnnotationsAsMap; @@ -887,7 +1001,8 @@ public class AnnotatedElementUtils { @Override public AnnotationAttributes process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { - boolean found = annotation.annotationType().getName().equals(this.annotationName); + boolean found = (this.annotationType != null ? annotation.annotationType() == this.annotationType : + annotation.annotationType().getName().equals(this.annotationName)); return (found ? AnnotationUtils.getAnnotationAttributes(annotatedElement, annotation, this.classValuesAsString, this.nestedAnnotationsAsMap, true) : null); }