/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.core.BridgeMethodResolver;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

General utility methods for finding annotations, meta-annotations, and repeatable annotations on AnnotatedElements.

AnnotatedElementUtils defines the public API for Spring's meta-annotation programming model with support for annotation attribute overrides. If you do not need support for annotation attribute overrides, consider using AnnotationUtils instead.

Note that the features of this class are not provided by the JDK's introspection facilities themselves.

Annotation Attribute Overrides

Support for meta-annotations with attribute overrides in composed annotations is provided by all variants of the getMergedAnnotationAttributes(), getMergedAnnotation(), getAllMergedAnnotations(), getMergedRepeatableAnnotations(), findMergedAnnotationAttributes(), findMergedAnnotation(), findAllMergedAnnotations(), and findMergedRepeatableAnnotations() methods.

Find vs. Get Semantics

The search algorithms used by methods in this class follow either 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 that are either present on an AnnotatedElement (i.e. declared locally or inherited) or declared within the annotation hierarchy above the AnnotatedElement.

Find semantics are much more exhaustive, providing get semantics plus support for the following:

  • Searching on interfaces, if the annotated element is a class
  • Searching on superclasses, if the annotated element is a class
  • Resolving bridged methods, if the annotated element is a method
  • Searching on methods in interfaces, if the annotated element is a method
  • Searching on methods in superclasses, if the annotated element is a method

Support for @Inherited

Methods following get semantics will honor the contract of Java's @Inherited annotation except that locally declared annotations (including custom composed annotations) will be favored over inherited annotations. In contrast, methods following find semantics will completely ignore the presence of @Inherited since the find search algorithm manually traverses type and method hierarchies and thereby implicitly supports annotation inheritance without a need for @Inherited.

Author:Phillip Webb, Juergen Hoeller, Sam Brannen
See Also:
Since:4.0
/** * General utility methods for finding annotations, meta-annotations, and * repeatable annotations on {@link AnnotatedElement AnnotatedElements}. * * <p>{@code AnnotatedElementUtils} defines the public API for Spring's * meta-annotation programming model with support for <em>annotation attribute * overrides</em>. If you do not need support for annotation attribute * overrides, consider using {@link AnnotationUtils} instead. * * <p>Note that the features of this class are not provided by the JDK's * introspection facilities themselves. * * <h3>Annotation Attribute Overrides</h3> * <p>Support for meta-annotations with <em>attribute overrides</em> in * <em>composed annotations</em> is provided by all variants of the * {@code getMergedAnnotationAttributes()}, {@code getMergedAnnotation()}, * {@code getAllMergedAnnotations()}, {@code getMergedRepeatableAnnotations()}, * {@code findMergedAnnotationAttributes()}, {@code findMergedAnnotation()}, * {@code findAllMergedAnnotations()}, and {@code findMergedRepeatableAnnotations()} * methods. * * <h3>Find vs. Get Semantics</h3> * <p>The search algorithms used by methods in this class follow either * <em>find</em> or <em>get</em> semantics. Consult the javadocs for each * individual method for details on which search algorithm is used. * * <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 <em>above</em> the {@code AnnotatedElement}. * * <p><strong>Find semantics</strong> are much more exhaustive, providing * <em>get semantics</em> plus support for the following: * * <ul> * <li>Searching on interfaces, if the annotated element is a class * <li>Searching on superclasses, if the annotated element is a class * <li>Resolving bridged methods, if the annotated element is a method * <li>Searching on methods in interfaces, if the annotated element is a method * <li>Searching on methods in superclasses, if the annotated element is a method * </ul> * * <h3>Support for {@code @Inherited}</h3> * <p>Methods following <em>get semantics</em> will honor the contract of Java's * {@link java.lang.annotation.Inherited @Inherited} annotation except that locally * declared annotations (including custom composed annotations) will be favored over * inherited annotations. In contrast, methods following <em>find semantics</em> * will completely ignore the presence of {@code @Inherited} since the <em>find</em> * search algorithm manually traverses type and method hierarchies and thereby * implicitly supports annotation inheritance without a need for {@code @Inherited}. * * @author Phillip Webb * @author Juergen Hoeller * @author Sam Brannen * @since 4.0 * @see AliasFor * @see AnnotationAttributes * @see AnnotationUtils * @see BridgeMethodResolver */
public abstract class AnnotatedElementUtils {
null constant used to denote that the search algorithm should continue.
/** * {@code null} constant used to denote that the search algorithm should continue. */
@Nullable private static final Boolean CONTINUE = null; private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; private static final Processor<Boolean> alwaysTrueAnnotationProcessor = new AlwaysTrueBooleanAnnotationProcessor();
Build an adapted AnnotatedElement for the given annotations, typically for use with other methods on AnnotatedElementUtils.
Params:
  • annotations – the annotations to expose through the AnnotatedElement
Since:4.3
/** * Build an adapted {@link AnnotatedElement} for the given annotations, * typically for use with other methods on {@link AnnotatedElementUtils}. * @param annotations the annotations to expose through the {@code AnnotatedElement} * @since 4.3 */
public static AnnotatedElement forAnnotations(final Annotation... annotations) { return new AnnotatedElement() { @Override @SuppressWarnings("unchecked") @Nullable public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { for (Annotation ann : annotations) { if (ann.annotationType() == annotationClass) { return (T) ann; } } return null; } @Override public Annotation[] getAnnotations() { return annotations; } @Override public Annotation[] getDeclaredAnnotations() { return annotations; } }; }
Get the fully qualified class names of all meta-annotation types present on the annotation (of the specified annotationType) on the supplied AnnotatedElement.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationType – the annotation type on which to find meta-annotations
See Also:
Returns:the names of all meta-annotations present on the annotation, or null if not found
Since:4.2
/** * Get the fully qualified class names of all meta-annotation types * <em>present</em> on the annotation (of the specified {@code annotationType}) * on the supplied {@link AnnotatedElement}. * <p>This method follows <em>get semantics</em> as described in the * {@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 * @see #getMetaAnnotationTypes(AnnotatedElement, String) * @see #hasMetaAnnotationTypes */
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) { return getMetaAnnotationTypes(element, element.getAnnotation(annotationType)); }
Get the fully qualified class names of all meta-annotation types present on the annotation (of the specified annotationName) on the supplied AnnotatedElement.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationName – the fully qualified class name of the annotation type on which to find meta-annotations
See Also:
Returns:the names of all meta-annotations present on the annotation, or an empty set if none found
/** * Get the fully qualified class names of all meta-annotation * types <em>present</em> on the annotation (of the specified * {@code annotationName}) on the supplied {@link AnnotatedElement}. * <p>This method follows <em>get semantics</em> 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 on which to find meta-annotations * @return the names of all meta-annotations present on the annotation, * or an empty set if none found * @see #getMetaAnnotationTypes(AnnotatedElement, Class) * @see #hasMetaAnnotationTypes */
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) { return getMetaAnnotationTypes(element, AnnotationUtils.getAnnotation(element, annotationName)); } private static Set<String> getMetaAnnotationTypes(AnnotatedElement element, @Nullable Annotation composed) { if (composed == null) { return Collections.emptySet(); } try { final Set<String> types = new LinkedHashSet<>(); searchWithGetSemantics(composed.annotationType(), Collections.emptySet(), null, null, new SimpleAnnotationProcessor<Object>(true) { @Override @Nullable public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { types.add(annotation.annotationType().getName()); return CONTINUE; } }, new HashSet<>(), 1); return types; } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); throw new IllegalStateException("Failed to introspect annotations on " + element, ex); } }
Determine if the supplied AnnotatedElement is annotated with a composed annotation that is meta-annotated with an annotation of the specified annotationType.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationType – the meta-annotation type to find
See Also:
Returns:true if a matching meta-annotation is present
Since:4.2.3
/** * Determine if the supplied {@link AnnotatedElement} is annotated with * a <em>composed annotation</em> that is meta-annotated with an * annotation of the specified {@code annotationType}. * <p>This method follows <em>get semantics</em> as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the meta-annotation type to find * @return {@code true} if a matching meta-annotation is present * @since 4.2.3 * @see #getMetaAnnotationTypes */
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) { return hasMetaAnnotationTypes(element, annotationType, null); }
Determine if the supplied AnnotatedElement is annotated with a composed annotation that is meta-annotated with an annotation of the specified annotationName.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationName – the fully qualified class name of the meta-annotation type to find
See Also:
Returns:true if a matching meta-annotation is present
/** * Determine if the supplied {@link AnnotatedElement} is annotated with a * <em>composed annotation</em> that is meta-annotated with an annotation * of the specified {@code annotationName}. * <p>This method follows <em>get semantics</em> 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 */
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) { return hasMetaAnnotationTypes(element, null, annotationName); } private static boolean hasMetaAnnotationTypes( AnnotatedElement element, @Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName) { return Boolean.TRUE.equals( searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() { @Override @Nullable public Boolean process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { return (metaDepth > 0 ? Boolean.TRUE : CONTINUE); } })); }
Determine if an annotation of the specified annotationType is present on the supplied AnnotatedElement or within the annotation hierarchy above the specified element.

If this method returns true, then getMergedAnnotationAttributes will return a non-null value.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationType – the annotation type to find
See Also:
Returns:true if a matching annotation is present
Since:4.2.3
/** * Determine if an annotation of the specified {@code annotationType} * is <em>present</em> on the supplied {@link AnnotatedElement} or * within the annotation hierarchy <em>above</em> the specified element. * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} * will return a non-null value. * <p>This method follows <em>get semantics</em> as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the annotation type to find * @return {@code true} if a matching annotation is present * @since 4.2.3 * @see #hasAnnotation(AnnotatedElement, Class) */
public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType) { // Shortcut: directly present on the element, with no processing needed? if (element.isAnnotationPresent(annotationType)) { return true; } return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor)); }
Determine if an annotation of the specified annotationName is present on the supplied AnnotatedElement or within the annotation hierarchy above the specified element.

If this method returns true, then getMergedAnnotationAttributes will return a non-null value.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationName – the fully qualified class name of the annotation type to find
Returns:true if a matching annotation is present
/** * Determine if an annotation of the specified {@code annotationName} is * <em>present</em> on the supplied {@link AnnotatedElement} or within the * annotation hierarchy <em>above</em> the specified element. * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} * will return a non-null value. * <p>This method follows <em>get semantics</em> 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, String annotationName) { return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, alwaysTrueAnnotationProcessor)); }
Get the first annotation of the specified annotationType within the annotation hierarchy above the supplied element and merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy.

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

This method delegates to getMergedAnnotationAttributes(AnnotatedElement, String).

Params:
  • element – the annotated element
  • annotationType – the annotation type to find
See Also:
Returns:the merged AnnotationAttributes, or null if not found
Since:4.2
/** * 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>{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}. * @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) * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #findMergedAnnotation(AnnotatedElement, Class) */
@Nullable public static AnnotationAttributes getMergedAnnotationAttributes( AnnotatedElement element, Class<? extends Annotation> annotationType) { AnnotationAttributes attributes = searchWithGetSemantics(element, annotationType, null, new MergedAnnotationAttributesProcessor()); AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false); return attributes; }
Get the first annotation of the specified annotationName within the annotation hierarchy above the supplied element and merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy.

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

This method delegates to getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean), supplying false for classValuesAsString and nestedAnnotationsAsMap.

Params:
  • element – the annotated element
  • annotationName – the fully qualified class name of the annotation type to find
See Also:
Returns:the merged AnnotationAttributes, or null if not found
Since:4.2
/** * Get the first annotation of the specified {@code annotationName} 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>{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)}, * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}. * @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) * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #getAllAnnotationAttributes(AnnotatedElement, String) */
@Nullable public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName) { return getMergedAnnotationAttributes(element, annotationName, false, false); }
Get the first annotation of the specified annotationName within the annotation hierarchy above the supplied 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 @AliasFor semantics are fully supported, both within a single annotation and within the annotation hierarchy.

In contrast to getAllAnnotationAttributes, the search algorithm used by this method will stop searching the annotation hierarchy once the first annotation of the specified annotationName has been found. As a consequence, additional annotations of the specified annotationName will be ignored.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationName – the fully qualified class name of the annotation type to find
  • classValuesAsString – whether to convert Class references into Strings or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested Annotation instances into AnnotationAttributes maps or to preserve them as Annotation instances
See Also:
Returns:the merged AnnotationAttributes, or null if not found
Since:4.2
/** * Get the first annotation of the specified {@code annotationName} 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, and {@link AliasFor @AliasFor} semantics are * fully supported, both within a single annotation and within the annotation hierarchy. * <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 annotationName} has been found. As a consequence, * additional annotations of the specified {@code annotationName} will be ignored. * <p>This method follows <em>get semantics</em> 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 #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */
@Nullable public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName, new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap)); AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); return attributes; }
Get the first annotation of the specified annotationType within the annotation hierarchy above the supplied element, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy, and synthesize the result back into an annotation of the specified annotationType.

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

This method delegates to getMergedAnnotationAttributes(AnnotatedElement, Class<? extends Annotation>) and AnnotationUtils.synthesizeAnnotation(Map<String,Object>, Class<Annotation>, AnnotatedElement).

Params:
  • element – the annotated element
  • annotationType – the annotation type to find
See Also:
Returns:the merged, synthesized Annotation, or null if not found
Since:4.2
/** * Get the first annotation of the specified {@code annotationType} within * the annotation hierarchy <em>above</em> the supplied {@code element}, * merge that annotation's attributes with <em>matching</em> attributes from * annotations in lower levels of the annotation hierarchy, and synthesize * the result back into an annotation of the specified {@code annotationType}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, Class)} * and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}. * @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) * @see #findMergedAnnotation(AnnotatedElement, Class) * @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement) */
@Nullable public static <A extends Annotation> A getMergedAnnotation(AnnotatedElement element, Class<A> annotationType) { // Shortcut: directly present on the element, with no merging needed? A annotation = element.getDeclaredAnnotation(annotationType); if (annotation != null) { return AnnotationUtils.synthesizeAnnotation(annotation, element); } // Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types... if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) { return null; } // Exhaustive retrieval of merged annotation attributes... AnnotationAttributes attributes = getMergedAnnotationAttributes(element, annotationType); return (attributes != null ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null); }
Get all annotations of the specified annotationType within the annotation hierarchy above the supplied element; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType.

@AliasFor semantics are fully supported, both within a single annotation and within annotation hierarchies.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element (never null)
  • annotationType – the annotation type to find (never null)
See Also:
Returns:the set of all merged, synthesized Annotations found, or an empty set if none were found
Since:4.3
/** * Get <strong>all</strong> annotations of the specified {@code annotationType} * within the annotation hierarchy <em>above</em> the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * <em>matching</em> attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. * <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 annotation type to find (never {@code null}) * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found * @since 4.3 * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #getAllAnnotationAttributes(AnnotatedElement, String) * @see #findAllMergedAnnotations(AnnotatedElement, Class) */
public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) { MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); searchWithGetSemantics(element, annotationType, null, processor); return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); }
Get all annotations of the specified annotationTypes within the annotation hierarchy above the supplied element; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the corresponding annotationType.

@AliasFor semantics are fully supported, both within a single annotation and within annotation hierarchies.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element (never null)
  • annotationTypes – the annotation types to find
See Also:
Returns:the set of all merged, synthesized Annotations found, or an empty set if none were found
Since:5.1
/** * Get <strong>all</strong> annotations of the specified {@code annotationTypes} * within the annotation hierarchy <em>above</em> the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * <em>matching</em> attributes from annotations in lower levels of the * annotation hierarchy and synthesize the results back into an annotation * of the corresponding {@code annotationType}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. * <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 annotationTypes the annotation types to find * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found * @since 5.1 * @see #getAllMergedAnnotations(AnnotatedElement, Class) */
public static Set<Annotation> getAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) { MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); searchWithGetSemantics(element, annotationTypes, null, null, processor); return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); }
Get all repeatable annotations of the specified annotationType within the annotation hierarchy above the supplied element; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType.

The container type that holds the repeatable annotations will be looked up via Repeatable.

@AliasFor semantics are fully supported, both within a single annotation and within annotation hierarchies.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element (never null)
  • annotationType – the annotation type to find (never null)
Throws:
See Also:
Returns:the set of all merged repeatable Annotations found, or an empty set if none were found
Since:4.3
/** * Get all <em>repeatable annotations</em> of the specified {@code annotationType} * within the annotation hierarchy <em>above</em> the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * <em>matching</em> attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. * <p>The container type that holds the repeatable annotations will be looked up * via {@link java.lang.annotation.Repeatable}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. * <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 annotation type to find (never {@code null}) * @return the set of all merged repeatable {@code Annotations} found, * or an empty set if none were found * @throws IllegalArgumentException if the {@code element} or {@code annotationType} * is {@code null}, or if the container type cannot be resolved * @since 4.3 * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #getAllMergedAnnotations(AnnotatedElement, Class) * @see #getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) */
public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(AnnotatedElement element, Class<A> annotationType) { return getMergedRepeatableAnnotations(element, annotationType, null); }
Get all repeatable annotations of the specified annotationType within the annotation hierarchy above the supplied element; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType.

@AliasFor semantics are fully supported, both within a single annotation and within annotation hierarchies.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element (never null)
  • annotationType – the annotation type to find (never null)
  • containerType – the type of the container that holds the annotations; may be null if the container type should be looked up via Repeatable
Throws:
See Also:
Returns:the set of all merged repeatable Annotations found, or an empty set if none were found
Since:4.3
/** * Get all <em>repeatable annotations</em> of the specified {@code annotationType} * within the annotation hierarchy <em>above</em> the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * <em>matching</em> attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. * <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 annotation type to find (never {@code null}) * @param containerType the type of the container that holds the annotations; * may be {@code null} if the container type should be looked up via * {@link java.lang.annotation.Repeatable} * @return the set of all merged repeatable {@code Annotations} found, * or an empty set if none were found * @throws IllegalArgumentException if the {@code element} or {@code annotationType} * is {@code null}, or if the container type cannot be resolved * @throws AnnotationConfigurationException if the supplied {@code containerType} * is not a valid container annotation for the supplied {@code annotationType} * @since 4.3 * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #getAllMergedAnnotations(AnnotatedElement, Class) */
public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(AnnotatedElement element, Class<A> annotationType, @Nullable Class<? extends Annotation> containerType) { if (containerType == null) { containerType = resolveContainerType(annotationType); } else { validateContainerType(annotationType, containerType); } MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); searchWithGetSemantics(element, Collections.singleton(annotationType), null, containerType, processor); return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); }
Get the annotation attributes of all annotations of the specified annotationName in the annotation hierarchy above the supplied AnnotatedElement and store the results in a MultiValueMap.

Note: in contrast to getMergedAnnotationAttributes(AnnotatedElement, String), this method does not support attribute overrides.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationName – the fully qualified class name of the annotation type to find
See Also:
Returns:a MultiValueMap keyed by attribute name, containing the annotation attributes from all annotations found, or null if not found
/** * Get the annotation attributes of <strong>all</strong> annotations of the specified * {@code annotationName} in the annotation hierarchy above the supplied * {@link AnnotatedElement} and store the results in a {@link MultiValueMap}. * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, * this method does <em>not</em> support attribute overrides. * <p>This method follows <em>get semantics</em> 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 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) */
@Nullable public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationName) { return getAllAnnotationAttributes(element, annotationName, false, false); }
Get the annotation attributes of all annotations of the specified annotationName in the annotation hierarchy above the supplied AnnotatedElement and store the results in a MultiValueMap.

Note: in contrast to getMergedAnnotationAttributes(AnnotatedElement, String), this method does not support attribute overrides.

This method follows get semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationName – the fully qualified class name of the annotation type to find
  • classValuesAsString – whether to convert Class references into Strings or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested Annotation instances into AnnotationAttributes maps or to preserve them as Annotation instances
Returns:a MultiValueMap keyed by attribute name, containing the annotation attributes from all annotations found, or null if not found
/** * Get the annotation attributes of <strong>all</strong> annotations of * the specified {@code annotationName} in the annotation hierarchy above * the supplied {@link AnnotatedElement} and store the results in a * {@link MultiValueMap}. * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, * this method does <em>not</em> support attribute overrides. * <p>This method follows <em>get semantics</em> 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 a {@link MultiValueMap} keyed by attribute name, containing the annotation * attributes from all annotations found, or {@code null} if not found */
@Nullable public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { final MultiValueMap<String, Object> attributesMap = new LinkedMultiValueMap<>(); searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor<Object>() { @Override @Nullable public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes( annotation, classValuesAsString, nestedAnnotationsAsMap); annotationAttributes.forEach(attributesMap::add); return CONTINUE; } }); return (!attributesMap.isEmpty() ? attributesMap : null); }
Determine if an annotation of the specified annotationType is available on the supplied AnnotatedElement or within the annotation hierarchy above the specified element.

If this method returns true, then findMergedAnnotationAttributes will return a non-null value.

This method follows find semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationType – the annotation type to find
See Also:
Returns:true if a matching annotation is present
Since:4.3
/** * Determine if an annotation of the specified {@code annotationType} * is <em>available</em> on the supplied {@link AnnotatedElement} or * within the annotation hierarchy <em>above</em> the specified element. * <p>If this method returns {@code true}, then {@link #findMergedAnnotationAttributes} * will return a non-null value. * <p>This method follows <em>find semantics</em> as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the annotation type to find * @return {@code true} if a matching annotation is present * @since 4.3 * @see #isAnnotated(AnnotatedElement, Class) */
public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) { // Shortcut: directly present on the element, with no processing needed? if (element.isAnnotationPresent(annotationType)) { return true; } return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor)); }
Find the first annotation of the specified annotationType within the annotation hierarchy above the supplied 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 @AliasFor semantics are fully supported, both within a single annotation and within the annotation hierarchy.

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

This method follows find semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationType – the annotation type to find
  • classValuesAsString – whether to convert Class references into Strings or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested Annotation instances into AnnotationAttributes maps or to preserve them as Annotation instances
See Also:
Returns:the merged AnnotationAttributes, or null if not found
Since:4.2
/** * 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, and * {@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. * <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}. * @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 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) */
@Nullable public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { AnnotationAttributes attributes = searchWithFindSemantics(element, annotationType, null, new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap)); AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); return attributes; }
Find the first annotation of the specified annotationName within the annotation hierarchy above the supplied 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 @AliasFor semantics are fully supported, both within a single annotation and within the annotation hierarchy.

In contrast to getAllAnnotationAttributes, the search algorithm used by this method will stop searching the annotation hierarchy once the first annotation of the specified annotationName has been found. As a consequence, additional annotations of the specified annotationName will be ignored.

This method follows find semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationName – the fully qualified class name of the annotation type to find
  • classValuesAsString – whether to convert Class references into Strings or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested Annotation instances into AnnotationAttributes maps or to preserve them as Annotation instances
See Also:
Returns:the merged AnnotationAttributes, or null if not found
Since:4.2
/** * Find the first annotation of the specified {@code annotationName} 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, and * {@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. * <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 annotationName} has been found. As a consequence, additional * annotations of the specified {@code annotationName} will be ignored. * <p>This method follows <em>find semantics</em> 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) */
@Nullable public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { AnnotationAttributes attributes = searchWithFindSemantics(element, null, annotationName, new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap)); AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); return attributes; }
Find the first annotation of the specified annotationType within the annotation hierarchy above the supplied element, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy, and synthesize the result back into an annotation of the specified annotationType.

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

This method follows find semantics as described in the class-level javadoc.

Params:
  • element – the annotated element
  • annotationType – the annotation type to find
See Also:
Returns:the merged, synthesized Annotation, or null if not found
Since:4.2
/** * Find the first annotation of the specified {@code annotationType} within * the annotation hierarchy <em>above</em> the supplied {@code element}, * merge that annotation's attributes with <em>matching</em> attributes from * annotations in lower levels of the annotation hierarchy, and synthesize * the result back into an annotation of the specified {@code annotationType}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. * <p>This method follows <em>find semantics</em> as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @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 #findAllMergedAnnotations(AnnotatedElement, Class) * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #getMergedAnnotationAttributes(AnnotatedElement, Class) */
@Nullable public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) { // Shortcut: directly present on the element, with no merging needed? A annotation = element.getDeclaredAnnotation(annotationType); if (annotation != null) { return AnnotationUtils.synthesizeAnnotation(annotation, element); } // Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types... if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) { return null; } // Exhaustive retrieval of merged annotation attributes... AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationType, false, false); return (attributes != null ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null); }
Find all annotations of the specified annotationType within the annotation hierarchy above the supplied element; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType.

@AliasFor semantics are fully supported, both within a single annotation and within annotation hierarchies.

This method follows find semantics as described in the class-level javadoc.

Params:
  • element – the annotated element (never null)
  • annotationType – the annotation type to find (never null)
See Also:
Returns:the set of all merged, synthesized Annotations found, or an empty set if none were found
Since:4.3
/** * Find <strong>all</strong> annotations of the specified {@code annotationType} * within the annotation hierarchy <em>above</em> the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * <em>matching</em> attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. * <p>This method follows <em>find semantics</em> as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found * @since 4.3 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #getAllMergedAnnotations(AnnotatedElement, Class) */
public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) { MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); searchWithFindSemantics(element, annotationType, null, processor); return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); }
Find all annotations of the specified annotationTypes within the annotation hierarchy above the supplied element; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the corresponding annotationType.

@AliasFor semantics are fully supported, both within a single annotation and within annotation hierarchies.

This method follows find semantics as described in the class-level javadoc.

Params:
  • element – the annotated element (never null)
  • annotationTypes – the annotation types to find
See Also:
Returns:the set of all merged, synthesized Annotations found, or an empty set if none were found
Since:5.1
/** * Find <strong>all</strong> annotations of the specified {@code annotationTypes} * within the annotation hierarchy <em>above</em> the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * <em>matching</em> attributes from annotations in lower levels of the * annotation hierarchy and synthesize the results back into an annotation * of the corresponding {@code annotationType}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. * <p>This method follows <em>find semantics</em> as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationTypes the annotation types to find * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found * @since 5.1 * @see #findAllMergedAnnotations(AnnotatedElement, Class) */
public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) { MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); searchWithFindSemantics(element, annotationTypes, null, null, processor); return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); }
Find all repeatable annotations of the specified annotationType within the annotation hierarchy above the supplied element; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType.

The container type that holds the repeatable annotations will be looked up via Repeatable.

@AliasFor semantics are fully supported, both within a single annotation and within annotation hierarchies.

This method follows find semantics as described in the class-level javadoc.

Params:
  • element – the annotated element (never null)
  • annotationType – the annotation type to find (never null)
Throws:
See Also:
Returns:the set of all merged repeatable Annotations found, or an empty set if none were found
Since:4.3
/** * Find all <em>repeatable annotations</em> of the specified {@code annotationType} * within the annotation hierarchy <em>above</em> the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * <em>matching</em> attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. * <p>The container type that holds the repeatable annotations will be looked up * via {@link java.lang.annotation.Repeatable}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. * <p>This method follows <em>find semantics</em> as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @return the set of all merged repeatable {@code Annotations} found, * or an empty set if none were found * @throws IllegalArgumentException if the {@code element} or {@code annotationType} * is {@code null}, or if the container type cannot be resolved * @since 4.3 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #findAllMergedAnnotations(AnnotatedElement, Class) * @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class) */
public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element, Class<A> annotationType) { return findMergedRepeatableAnnotations(element, annotationType, null); }
Find all repeatable annotations of the specified annotationType within the annotation hierarchy above the supplied element; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType.

@AliasFor semantics are fully supported, both within a single annotation and within annotation hierarchies.

This method follows find semantics as described in the class-level javadoc.

Params:
  • element – the annotated element (never null)
  • annotationType – the annotation type to find (never null)
  • containerType – the type of the container that holds the annotations; may be null if the container type should be looked up via Repeatable
Throws:
See Also:
Returns:the set of all merged repeatable Annotations found, or an empty set if none were found
Since:4.3
/** * Find all <em>repeatable annotations</em> of the specified {@code annotationType} * within the annotation hierarchy <em>above</em> the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * <em>matching</em> attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. * <p>This method follows <em>find semantics</em> as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @param containerType the type of the container that holds the annotations; * may be {@code null} if the container type should be looked up via * {@link java.lang.annotation.Repeatable} * @return the set of all merged repeatable {@code Annotations} found, * or an empty set if none were found * @throws IllegalArgumentException if the {@code element} or {@code annotationType} * is {@code null}, or if the container type cannot be resolved * @throws AnnotationConfigurationException if the supplied {@code containerType} * is not a valid container annotation for the supplied {@code annotationType} * @since 4.3 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #findAllMergedAnnotations(AnnotatedElement, Class) */
public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element, Class<A> annotationType, @Nullable Class<? extends Annotation> containerType) { if (containerType == null) { containerType = resolveContainerType(annotationType); } else { validateContainerType(annotationType, containerType); } MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); searchWithFindSemantics(element, Collections.singleton(annotationType), null, containerType, processor); return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); }
Search for annotations of the specified annotationName or annotationType on the specified element, following get semantics.
Params:
  • element – the annotated element
  • annotationType – the annotation type to find
  • annotationName – the fully qualified class name of the annotation type to find (as an alternative to annotationType)
  • processor – the processor to delegate to
Returns:the result of the processor (potentially null)
/** * Search for annotations of the specified {@code annotationName} or * {@code annotationType} on the specified {@code element}, following * <em>get semantics</em>. * @param element the annotated element * @param annotationType the annotation type to find * @param annotationName the fully qualified class name of the annotation * 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}) */
@Nullable private static <T> T searchWithGetSemantics(AnnotatedElement element, @Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName, Processor<T> processor) { return searchWithGetSemantics(element, (annotationType != null ? Collections.singleton(annotationType) : Collections.emptySet()), annotationName, null, processor); }
Search for annotations of the specified annotationName or annotationType on the specified element, following get semantics.
Params:
  • element – the annotated element
  • annotationTypes – the annotation types to find
  • annotationName – the fully qualified class name of the annotation type to find (as an alternative to annotationType)
  • containerType – the type of the container that holds repeatable annotations, or null if the annotation is not repeatable
  • processor – the processor to delegate to
Returns:the result of the processor (potentially null)
Since:4.3
/** * Search for annotations of the specified {@code annotationName} or * {@code annotationType} on the specified {@code element}, following * <em>get semantics</em>. * @param element the annotated element * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @param processor the processor to delegate to * @return the result of the processor (potentially {@code null}) * @since 4.3 */
@Nullable private static <T> T searchWithGetSemantics(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName, @Nullable Class<? extends Annotation> containerType, Processor<T> processor) { try { return searchWithGetSemantics(element, annotationTypes, annotationName, containerType, processor, new HashSet<>(), 0); } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); throw new IllegalStateException("Failed to introspect annotations on " + element, ex); } }
Perform the search algorithm for the searchWithGetSemantics method, avoiding endless recursion by tracking which annotated elements have already been visited.

The metaDepth parameter is explained in the process() method of the Processor API.

Params:
  • element – the annotated element
  • annotationTypes – the annotation types to find
  • annotationName – the fully qualified class name of the annotation type to find (as an alternative to annotationType)
  • containerType – the type of the container that holds repeatable annotations, or null if the annotation is not repeatable
  • processor – the processor to delegate to
  • visited – the set of annotated elements that have already been visited
  • metaDepth – the meta-depth of the annotation
Returns:the result of the processor (potentially null)
/** * Perform the search algorithm for the {@link #searchWithGetSemantics} * method, avoiding endless recursion by tracking which annotated elements * have already been <em>visited</em>. * <p>The {@code metaDepth} parameter is explained in the * {@link Processor#process process()} method of the {@link Processor} API. * @param element the annotated element * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @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}) */
@Nullable private static <T> T searchWithGetSemantics(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName, @Nullable Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) { if (visited.add(element)) { try { // Start searching within locally declared annotations List<Annotation> declaredAnnotations = Arrays.asList(AnnotationUtils.getDeclaredAnnotations(element)); T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new Class<?> superclass = ((Class<?>) element).getSuperclass(); if (superclass != null && superclass != Object.class) { List<Annotation> inheritedAnnotations = new LinkedList<>(); for (Annotation annotation : element.getAnnotations()) { if (!declaredAnnotations.contains(annotation)) { inheritedAnnotations.add(annotation); } } // Continue searching within inherited annotations result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } } } } catch (Throwable ex) { AnnotationUtils.handleIntrospectionFailure(element, ex); } } return null; }
This method is invoked by searchWithGetSemantics 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 local annotations to take precedence over inherited annotations.

The metaDepth parameter is explained in the process() method of the Processor API.

Params:
  • element – the element that is annotated with the supplied annotations, used for contextual logging; may be null if unknown
  • annotations – the annotations to search in
  • annotationTypes – the annotation types to find
  • annotationName – the fully qualified class name of the annotation type to find (as an alternative to annotationType)
  • containerType – the type of the container that holds repeatable annotations, or null if the annotation is not repeatable
  • processor – the processor to delegate to
  • visited – the set of annotated elements that have already been visited
  • metaDepth – the meta-depth of the annotation
Returns:the result of the processor (potentially null)
Since:4.2
/** * This method is invoked by {@link #searchWithGetSemantics} to perform * the actual search within the supplied list of annotations. * <p>This method should be invoked first with locally declared annotations * and then subsequently with inherited annotations, thereby allowing * local annotations to take precedence over inherited annotations. * <p>The {@code metaDepth} parameter is explained in the * {@link Processor#process process()} method of the {@link Processor} API. * @param element 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 * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @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 */
@Nullable private static <T> T searchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element, List<Annotation> annotations, Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName, @Nullable Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) { // Search in annotations for (Annotation annotation : annotations) { Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { if (annotationTypes.contains(currentAnnotationType) || currentAnnotationType.getName().equals(annotationName) || processor.alwaysProcesses()) { T result = processor.process(element, annotation, metaDepth); if (result != null) { if (processor.aggregates() && metaDepth == 0) { processor.getAggregatedResults().add(result); } else { return result; } } } // Repeatable annotations in container? else if (currentAnnotationType == containerType) { for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) { T result = processor.process(element, contained, metaDepth); if (result != null) { // No need to post-process since repeatable annotations within a // container cannot be composed annotations. processor.getAggregatedResults().add(result); } } } } } // Recursively search in meta-annotations for (Annotation annotation : annotations) { Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) { T result = searchWithGetSemantics(currentAnnotationType, annotationTypes, annotationName, containerType, processor, visited, metaDepth + 1); if (result != null) { processor.postProcess(element, annotation, result); if (processor.aggregates() && metaDepth == 0) { processor.getAggregatedResults().add(result); } else { return result; } } } } return null; }
Search for annotations of the specified annotationName or annotationType on the specified element, following find semantics.
Params:
  • element – the annotated element
  • annotationType – the annotation type to find
  • annotationName – the fully qualified class name of the annotation type to find (as an alternative to annotationType)
  • processor – the processor to delegate to
Returns:the result of the processor (potentially null)
Since:4.2
/** * Search for annotations of the specified {@code annotationName} or * {@code annotationType} on the specified {@code element}, following * <em>find semantics</em>. * @param element the annotated element * @param annotationType the annotation type to find * @param annotationName the fully qualified class name of the annotation * 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 */
@Nullable private static <T> T searchWithFindSemantics(AnnotatedElement element, @Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName, Processor<T> processor) { return searchWithFindSemantics(element, (annotationType != null ? Collections.singleton(annotationType) : Collections.emptySet()), annotationName, null, processor); }
Search for annotations of the specified annotationName or annotationType on the specified element, following find semantics.
Params:
  • element – the annotated element
  • annotationTypes – the annotation types to find
  • annotationName – the fully qualified class name of the annotation type to find (as an alternative to annotationType)
  • containerType – the type of the container that holds repeatable annotations, or null if the annotation is not repeatable
  • processor – the processor to delegate to
Returns:the result of the processor (potentially null)
Since:4.3
/** * Search for annotations of the specified {@code annotationName} or * {@code annotationType} on the specified {@code element}, following * <em>find semantics</em>. * @param element the annotated element * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @param processor the processor to delegate to * @return the result of the processor (potentially {@code null}) * @since 4.3 */
@Nullable private static <T> T searchWithFindSemantics(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName, @Nullable Class<? extends Annotation> containerType, Processor<T> processor) { if (containerType != null && !processor.aggregates()) { throw new IllegalArgumentException( "Searches for repeatable annotations must supply an aggregating Processor"); } try { return searchWithFindSemantics( element, annotationTypes, annotationName, containerType, processor, new HashSet<>(), 0); } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); throw new IllegalStateException("Failed to introspect annotations on " + element, ex); } }
Perform the search algorithm for the searchWithFindSemantics method, avoiding endless recursion by tracking which annotated elements have already been visited.

The metaDepth parameter is explained in the process() method of the Processor API.

Params:
  • element – the annotated element (never null)
  • annotationTypes – the annotation types to find
  • annotationName – the fully qualified class name of the annotation type to find (as an alternative to annotationType)
  • containerType – the type of the container that holds repeatable annotations, or null if the annotation is not repeatable
  • processor – the processor to delegate to
  • visited – the set of annotated elements that have already been visited
  • metaDepth – the meta-depth of the annotation
Returns:the result of the processor (potentially null)
Since:4.2
/** * Perform the search algorithm for the {@link #searchWithFindSemantics} * method, avoiding endless recursion by tracking which annotated elements * have already been <em>visited</em>. * <p>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 annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @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 */
@Nullable private static <T> T searchWithFindSemantics(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName, @Nullable Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) { if (visited.add(element)) { try { // Locally declared annotations (ignoring @Inherited) Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(element); if (annotations.length > 0) { List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null); // Search in local annotations for (Annotation annotation : annotations) { Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { if (annotationTypes.contains(currentAnnotationType) || currentAnnotationType.getName().equals(annotationName) || processor.alwaysProcesses()) { T result = processor.process(element, annotation, metaDepth); if (result != null) { if (aggregatedResults != null && metaDepth == 0) { aggregatedResults.add(result); } else { return result; } } } // Repeatable annotations in container? else if (currentAnnotationType == containerType) { for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) { T result = processor.process(element, contained, metaDepth); if (aggregatedResults != null && result != null) { // No need to post-process since repeatable annotations within a // container cannot be composed annotations. aggregatedResults.add(result); } } } } } // Recursively search in meta-annotations for (Annotation annotation : annotations) { Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) { T result = searchWithFindSemantics(currentAnnotationType, annotationTypes, annotationName, containerType, processor, visited, metaDepth + 1); if (result != null) { processor.postProcess(currentAnnotationType, annotation, result); if (aggregatedResults != null && metaDepth == 0) { aggregatedResults.add(result); } else { return result; } } } } if (!CollectionUtils.isEmpty(aggregatedResults)) { // Prepend to support top-down ordering within class hierarchies processor.getAggregatedResults().addAll(0, aggregatedResults); } } if (element instanceof Method) { Method method = (Method) element; T result; // Search on possibly bridged method Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); if (resolvedMethod != method) { result = searchWithFindSemantics(resolvedMethod, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } } // Search on methods in interfaces declared locally Class<?>[] ifcs = method.getDeclaringClass().getInterfaces(); if (ifcs.length > 0) { result = searchOnInterfaces(method, annotationTypes, annotationName, containerType, processor, visited, metaDepth, ifcs); if (result != null) { return result; } } // Search on methods in class hierarchy and interface hierarchy Class<?> clazz = method.getDeclaringClass(); while (true) { clazz = clazz.getSuperclass(); if (clazz == null || clazz == Object.class) { break; } Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz); if (!annotatedMethods.isEmpty()) { for (Method annotatedMethod : annotatedMethods) { if (AnnotationUtils.isOverride(method, annotatedMethod)) { Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod); result = searchWithFindSemantics(resolvedSuperMethod, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } } } } // Search on interfaces declared on superclass result = searchOnInterfaces(method, annotationTypes, annotationName, containerType, processor, visited, metaDepth, clazz.getInterfaces()); if (result != null) { return result; } } } else if (element instanceof Class) { Class<?> clazz = (Class<?>) element; if (!Annotation.class.isAssignableFrom(clazz)) { // Search on interfaces for (Class<?> ifc : clazz.getInterfaces()) { T result = searchWithFindSemantics(ifc, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } } // Search on superclass Class<?> superclass = clazz.getSuperclass(); if (superclass != null && superclass != Object.class) { T result = searchWithFindSemantics(superclass, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } } } } } catch (Throwable ex) { AnnotationUtils.handleIntrospectionFailure(element, ex); } } return null; } @Nullable private static <T> T searchOnInterfaces(Method method, Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName, @Nullable Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) { for (Class<?> ifc : ifcs) { Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(ifc); if (!annotatedMethods.isEmpty()) { for (Method annotatedMethod : annotatedMethods) { if (AnnotationUtils.isOverride(method, annotatedMethod)) { T result = searchWithFindSemantics(annotatedMethod, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } } } } } return null; }
Get the array of raw (unsynthesized) annotations from the value attribute of the supplied repeatable annotation container.
Since:4.3
/** * Get the array of raw (unsynthesized) annotations from the {@code value} * attribute of the supplied repeatable annotation {@code container}. * @since 4.3 */
@SuppressWarnings("unchecked") private static <A extends Annotation> A[] getRawAnnotationsFromContainer( @Nullable AnnotatedElement element, Annotation container) { try { A[] value = (A[]) AnnotationUtils.getValue(container); if (value != null) { return value; } } catch (Throwable ex) { AnnotationUtils.handleIntrospectionFailure(element, ex); } // Unable to read value from repeating annotation container -> ignore it. return (A[]) EMPTY_ANNOTATION_ARRAY; }
Resolve the container type for the supplied repeatable annotationType.

Delegates to AnnotationUtils.resolveContainerAnnotationType(Class<? extends Annotation>).

Params:
  • annotationType – the annotation type to resolve the container for
Throws:
Returns:the container type (never null)
Since:4.3
/** * Resolve the container type for the supplied repeatable {@code annotationType}. * <p>Delegates to {@link AnnotationUtils#resolveContainerAnnotationType(Class)}. * @param annotationType the annotation type to resolve the container for * @return the container type (never {@code null}) * @throws IllegalArgumentException if the container type cannot be resolved * @since 4.3 */
private static Class<? extends Annotation> resolveContainerType(Class<? extends Annotation> annotationType) { Class<? extends Annotation> containerType = AnnotationUtils.resolveContainerAnnotationType(annotationType); if (containerType == null) { throw new IllegalArgumentException( "Annotation type must be a repeatable annotation: failed to resolve container type for " + annotationType.getName()); } return containerType; }
Validate that the supplied containerType is a proper container annotation for the supplied repeatable annotationType (i.e. that it declares a value attribute that holds an array of the annotationType).
Throws:
Since:4.3
/** * Validate that the supplied {@code containerType} is a proper container * annotation for the supplied repeatable {@code annotationType} (i.e. * that it declares a {@code value} attribute that holds an array of the * {@code annotationType}). * @throws AnnotationConfigurationException if the supplied {@code containerType} * is not a valid container annotation for the supplied {@code annotationType} * @since 4.3 */
private static void validateContainerType(Class<? extends Annotation> annotationType, Class<? extends Annotation> containerType) { try { Method method = containerType.getDeclaredMethod(AnnotationUtils.VALUE); Class<?> returnType = method.getReturnType(); if (!returnType.isArray() || returnType.getComponentType() != annotationType) { String msg = String.format( "Container type [%s] must declare a 'value' attribute for an array of type [%s]", containerType.getName(), annotationType.getName()); throw new AnnotationConfigurationException(msg); } } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); String msg = String.format("Invalid declaration of container type [%s] for repeatable annotation [%s]", containerType.getName(), annotationType.getName()); throw new AnnotationConfigurationException(msg, ex); } }
Post-process the aggregated results into a set of synthesized annotations.
Params:
  • element – the annotated element
  • aggregatedResults – the aggregated results for the given element
Returns:the set of annotations
/** * Post-process the aggregated results into a set of synthesized annotations. * @param element the annotated element * @param aggregatedResults the aggregated results for the given element * @return the set of annotations */
@SuppressWarnings("unchecked") private static <A extends Annotation> Set<A> postProcessAndSynthesizeAggregatedResults( AnnotatedElement element, List<AnnotationAttributes> aggregatedResults) { Set<A> annotations = new LinkedHashSet<>(); for (AnnotationAttributes attributes : aggregatedResults) { AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false); Class<? extends Annotation> annType = attributes.annotationType(); if (annType != null) { annotations.add((A) AnnotationUtils.synthesizeAnnotation(attributes, annType, element)); } } return annotations; }
Callback interface that is used to process annotations during a search.

Depending on the use case, a processor may choose to process a single target annotation, multiple target annotations, or all annotations discovered by the currently executing search. The term "target" in this context refers to a matching annotation (i.e. a specific annotation type that was found during the search).

Returning a non-null value from the process method instructs the search algorithm to stop searching further; whereas, returning null from the process method instructs the search algorithm to continue searching for additional annotations. One exception to this rule applies to processors that aggregate results. If an aggregating processor returns a non-null value, that value will be added to the aggregated results and the search algorithm will continue.

Processors can optionally post-process the result of the process method as the search algorithm goes back down the annotation hierarchy from an invocation of process that returned a non-null value down to the AnnotatedElement that was supplied as the starting point to the search algorithm.

Type parameters:
  • <T> – the type of result returned by the processor
/** * Callback interface that is used to process annotations during a search. * <p>Depending on the use case, a processor may choose to {@linkplain #process} * a single target annotation, multiple target annotations, or all annotations * discovered by the currently executing search. The term "target" in this * context refers to a matching annotation (i.e. a specific annotation type * that was found during the search). * <p>Returning a non-null value from the {@link #process} method instructs * the search algorithm to stop searching further; whereas, returning * {@code null} from the {@link #process} method instructs the search * algorithm to continue searching for additional annotations. One exception * to this rule applies to processors that {@linkplain #aggregates aggregate} * results. If an aggregating processor returns a non-null value, that value * will be added to the {@linkplain #getAggregatedResults aggregated results} * and the search algorithm will continue. * <p>Processors can optionally {@linkplain #postProcess post-process} the * result of the {@link #process} method as the search algorithm goes back * down the annotation hierarchy from an invocation of {@link #process} that * returned a non-null value down to the {@link AnnotatedElement} that was * supplied as the starting point to the search algorithm. * @param <T> the type of result returned by the processor */
private interface Processor<T> {
Process the supplied annotation.

The supplied annotation will be an actual target annotation that has been found by the search algorithm, unless this processor is configured to always process annotations in which case it may be some other annotation within an annotation hierarchy. In the latter case, the metaDepth will have a value greater than 0. In any case, it is up to concrete implementations of this method to decide what to do with the supplied annotation.

The metaDepth parameter represents the depth of the annotation relative to the first annotated element in the annotation hierarchy. For example, an annotation that is present on a non-annotation element will have a depth of 0; a meta-annotation will have a depth of 1; and a meta-meta-annotation will have a depth of 2; etc.

Params:
  • annotatedElement – the element that is annotated with the supplied annotation, used for contextual logging; may be null if unknown
  • annotation – the annotation to process
  • metaDepth – the meta-depth of the annotation
Returns:the result of the processing, or null to continue searching for additional annotations
/** * Process the supplied annotation. * <p>The supplied annotation will be an actual target annotation * that has been found by the search algorithm, unless this processor * is configured to {@linkplain #alwaysProcesses always process} * annotations in which case it may be some other annotation within an * annotation hierarchy. In the latter case, the {@code metaDepth} * will have a value greater than {@code 0}. In any case, it is * up to concrete implementations of this method to decide what to * do with the supplied annotation. * <p>The {@code metaDepth} parameter represents the depth of the * annotation relative to the first annotated element in the * annotation hierarchy. For example, an annotation that is * <em>present</em> on a non-annotation element will have a depth * of 0; a meta-annotation will have a depth of 1; and a * meta-meta-annotation will have a depth of 2; etc. * @param annotatedElement the element that is annotated with the * supplied annotation, used for contextual logging; may be * {@code null} if unknown * @param annotation the annotation to process * @param metaDepth the meta-depth of the annotation * @return the result of the processing, or {@code null} to continue * searching for additional annotations */
@Nullable T process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth);
Post-process the result returned by the process method.

The annotation supplied to this method is an annotation that is present in the annotation hierarchy, between the initial AnnotatedElement and an invocation of process that returned a non-null value.

Params:
  • annotatedElement – the element that is annotated with the supplied annotation, used for contextual logging; may be null if unknown
  • annotation – the annotation to post-process
  • result – the result to post-process
/** * Post-process the result returned by the {@link #process} method. * <p>The {@code annotation} supplied to this method is an annotation * that is present in the annotation hierarchy, between the initial * {@link AnnotatedElement} and an invocation of {@link #process} * that returned a non-null value. * @param annotatedElement the element that is annotated with the * supplied annotation, used for contextual logging; may be * {@code null} if unknown * @param annotation the annotation to post-process * @param result the result to post-process */
void postProcess(@Nullable AnnotatedElement annotatedElement, Annotation annotation, T result);
Determine if this processor always processes annotations regardless of whether or not the target annotation has been found.
Returns:true if this processor always processes annotations
Since:4.3
/** * Determine if this processor always processes annotations regardless of * whether or not the target annotation has been found. * @return {@code true} if this processor always processes annotations * @since 4.3 */
boolean alwaysProcesses();
Determine if this processor aggregates the results returned by process.

If this method returns true, then getAggregatedResults() must return a non-null value.

See Also:
Returns:true if this processor supports aggregated results
Since:4.3
/** * Determine if this processor aggregates the results returned by {@link #process}. * <p>If this method returns {@code true}, then {@link #getAggregatedResults()} * must return a non-null value. * @return {@code true} if this processor supports aggregated results * @since 4.3 * @see #getAggregatedResults */
boolean aggregates();
Get the list of results aggregated by this processor.

NOTE: the processor does not aggregate the results itself. Rather, the search algorithm that uses this processor is responsible for asking this processor if it aggregates results and then adding the post-processed results to the list returned by this method.

See Also:
Returns:the list of results aggregated by this processor (never null)
Since:4.3
/** * Get the list of results aggregated by this processor. * <p>NOTE: the processor does <strong>not</strong> aggregate the results * itself. Rather, the search algorithm that uses this processor is * responsible for asking this processor if it {@link #aggregates} results * and then adding the post-processed results to the list returned by this * method. * @return the list of results aggregated by this processor (never {@code null}) * @since 4.3 * @see #aggregates */
List<T> getAggregatedResults(); }
Processor that processes annotations but does not post-process or aggregate results.
Since:4.2
/** * {@link Processor} that {@linkplain #process(AnnotatedElement, Annotation, int) * processes} annotations but does not {@linkplain #postProcess post-process} or * {@linkplain #aggregates aggregate} results. * @since 4.2 */
private abstract static class SimpleAnnotationProcessor<T> implements Processor<T> { private final boolean alwaysProcesses; public SimpleAnnotationProcessor() { this(false); } public SimpleAnnotationProcessor(boolean alwaysProcesses) { this.alwaysProcesses = alwaysProcesses; } @Override public final boolean alwaysProcesses() { return this.alwaysProcesses; } @Override public final void postProcess(@Nullable AnnotatedElement annotatedElement, Annotation annotation, T result) { // no-op } @Override public final boolean aggregates() { return false; } @Override public final List<T> getAggregatedResults() { throw new UnsupportedOperationException("SimpleAnnotationProcessor does not support aggregated results"); } }
SimpleAnnotationProcessor that always returns Boolean.TRUE when asked to process an annotation.
Since:4.3
/** * {@link SimpleAnnotationProcessor} that always returns {@link Boolean#TRUE} when * asked to {@linkplain #process(AnnotatedElement, Annotation, int) process} an * annotation. * @since 4.3 */
static class AlwaysTrueBooleanAnnotationProcessor extends SimpleAnnotationProcessor<Boolean> { @Override public final Boolean process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { return Boolean.TRUE; } }
Processor that gets the AnnotationAttributes for the target annotation during the process phase and then merges annotation attributes from lower levels in the annotation hierarchy during the postProcess phase.

A MergedAnnotationAttributesProcessor may optionally be configured to aggregate results.

See Also:
Since:4.2
/** * {@link Processor} that gets the {@code AnnotationAttributes} for the * target annotation during the {@link #process} phase and then merges * annotation attributes from lower levels in the annotation hierarchy * during the {@link #postProcess} phase. * <p>A {@code MergedAnnotationAttributesProcessor} may optionally be * configured to {@linkplain #aggregates aggregate} results. * @since 4.2 * @see AnnotationUtils#retrieveAnnotationAttributes * @see AnnotationUtils#postProcessAnnotationAttributes */
private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> { private final boolean classValuesAsString; private final boolean nestedAnnotationsAsMap; private final boolean aggregates; private final List<AnnotationAttributes> aggregatedResults; MergedAnnotationAttributesProcessor() { this(false, false, false); } MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) { this(classValuesAsString, nestedAnnotationsAsMap, false); } MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean aggregates) { this.classValuesAsString = classValuesAsString; this.nestedAnnotationsAsMap = nestedAnnotationsAsMap; this.aggregates = aggregates; this.aggregatedResults = (aggregates ? new ArrayList<>() : Collections.emptyList()); } @Override public boolean alwaysProcesses() { return false; } @Override public boolean aggregates() { return this.aggregates; } @Override public List<AnnotationAttributes> getAggregatedResults() { return this.aggregatedResults; } @Override @Nullable public AnnotationAttributes process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation, this.classValuesAsString, this.nestedAnnotationsAsMap); } @Override public void postProcess(@Nullable AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) { annotation = AnnotationUtils.synthesizeAnnotation(annotation, element); Class<? extends Annotation> targetAnnotationType = attributes.annotationType(); // Track which attribute values have already been replaced so that we can short // circuit the search algorithms. Set<String> valuesAlreadyReplaced = new HashSet<>(); for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) { String attributeName = attributeMethod.getName(); String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType); // Explicit annotation attribute override declared via @AliasFor if (attributeOverrideName != null) { if (valuesAlreadyReplaced.contains(attributeOverrideName)) { continue; } List<String> targetAttributeNames = new ArrayList<>(); targetAttributeNames.add(attributeOverrideName); valuesAlreadyReplaced.add(attributeOverrideName); // Ensure all aliased attributes in the target annotation are overridden. (SPR-14069) List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName); if (aliases != null) { for (String alias : aliases) { if (!valuesAlreadyReplaced.contains(alias)) { targetAttributeNames.add(alias); valuesAlreadyReplaced.add(alias); } } } overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames); } // Implicit annotation attribute override based on convention else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) { overrideAttribute(element, annotation, attributes, attributeName, attributeName); } } } private void overrideAttributes(@Nullable AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes, String sourceAttributeName, List<String> targetAttributeNames) { Object adaptedValue = getAdaptedValue(element, annotation, sourceAttributeName); for (String targetAttributeName : targetAttributeNames) { attributes.put(targetAttributeName, adaptedValue); } } private void overrideAttribute(@Nullable AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes, String sourceAttributeName, String targetAttributeName) { attributes.put(targetAttributeName, getAdaptedValue(element, annotation, sourceAttributeName)); } @Nullable private Object getAdaptedValue( @Nullable AnnotatedElement element, Annotation annotation, String sourceAttributeName) { Object value = AnnotationUtils.getValue(annotation, sourceAttributeName); return AnnotationUtils.adaptValue(element, value, this.classValuesAsString, this.nestedAnnotationsAsMap); } } }