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); }