/*
 * 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.annotation.Repeatable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

General utility methods for working with annotations, handling meta-annotations, bridge methods (which the compiler generates for generic declarations) as well as super methods (for optional annotation inheritance).

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

As a general rule for runtime-retained annotations (e.g. for transaction control, authorization, or service exposure), always use the lookup methods on this class (e.g., findAnnotation(Method, Class<Annotation>), getAnnotation(Method, Class<Annotation>), and getAnnotations(Method)) instead of the plain annotation lookup methods in the JDK. You can still explicitly choose between a get lookup on the given class level only (getAnnotation(Method, Class<Annotation>)) and a find lookup in the entire inheritance hierarchy of the given method (findAnnotation(Method, Class<Annotation>)).

Terminology

The terms directly present, indirectly present, and present have the same meanings as defined in the class-level javadoc for AnnotatedElement (in Java 8).

An annotation is meta-present on an element if the annotation is declared as a meta-annotation on some other annotation which is present on the element. Annotation A is meta-present on another annotation if A is either directly present or meta-present on the other annotation.

Meta-annotation Support

Most find*() methods and some get*() methods in this class provide support for finding annotations used as meta-annotations. Consult the javadoc for each method in this class for details. For fine-grained support for meta-annotations with attribute overrides in composed annotations, consider using AnnotatedElementUtils's more specific methods instead.

Attribute Aliases

All public methods in this class that return annotations, arrays of annotations, or AnnotationAttributes transparently support attribute aliases configured via @AliasFor. Consult the various synthesizeAnnotation*(..) methods for details.

Search Scope

The search algorithms used by methods in this class stop searching for an annotation once the first annotation of the specified type has been found. As a consequence, additional annotations of the specified type will be silently ignored.

Author:Rob Harrop, Juergen Hoeller, Sam Brannen, Mark Fisher, Chris Beams, Phillip Webb, Oleg Zhurakousky
See Also:
Since:2.0
/** * General utility methods for working with annotations, handling meta-annotations, * bridge methods (which the compiler generates for generic declarations) as well * as super methods (for optional <em>annotation inheritance</em>). * * <p>Note that most of the features of this class are not provided by the * JDK's introspection facilities themselves. * * <p>As a general rule for runtime-retained annotations (e.g. for transaction * control, authorization, or service exposure), always use the lookup methods * on this class (e.g., {@link #findAnnotation(Method, Class)}, * {@link #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)}) * instead of the plain annotation lookup methods in the JDK. You can still * explicitly choose between a <em>get</em> lookup on the given class level only * ({@link #getAnnotation(Method, Class)}) and a <em>find</em> lookup in the entire * inheritance hierarchy of the given method ({@link #findAnnotation(Method, Class)}). * * <h3>Terminology</h3> * The terms <em>directly present</em>, <em>indirectly present</em>, and * <em>present</em> have the same meanings as defined in the class-level * javadoc for {@link AnnotatedElement} (in Java 8). * * <p>An annotation is <em>meta-present</em> on an element if the annotation * is declared as a meta-annotation on some other annotation which is * <em>present</em> on the element. Annotation {@code A} is <em>meta-present</em> * on another annotation if {@code A} is either <em>directly present</em> or * <em>meta-present</em> on the other annotation. * * <h3>Meta-annotation Support</h3> * <p>Most {@code find*()} methods and some {@code get*()} methods in this class * provide support for finding annotations used as meta-annotations. Consult the * javadoc for each method in this class for details. For fine-grained support for * meta-annotations with <em>attribute overrides</em> in <em>composed annotations</em>, * consider using {@link AnnotatedElementUtils}'s more specific methods instead. * * <h3>Attribute Aliases</h3> * <p>All public methods in this class that return annotations, arrays of * annotations, or {@link AnnotationAttributes} transparently support attribute * aliases configured via {@link AliasFor @AliasFor}. Consult the various * {@code synthesizeAnnotation*(..)} methods for details. * * <h3>Search Scope</h3> * <p>The search algorithms used by methods in this class stop searching for * an annotation once the first annotation of the specified type has been * found. As a consequence, additional annotations of the specified type will * be silently ignored. * * @author Rob Harrop * @author Juergen Hoeller * @author Sam Brannen * @author Mark Fisher * @author Chris Beams * @author Phillip Webb * @author Oleg Zhurakousky * @since 2.0 * @see AliasFor * @see AnnotationAttributes * @see AnnotatedElementUtils * @see BridgeMethodResolver * @see java.lang.reflect.AnnotatedElement#getAnnotations() * @see java.lang.reflect.AnnotatedElement#getAnnotation(Class) * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations() */
public abstract class AnnotationUtils {
The attribute name for annotations with a single element.
/** * The attribute name for annotations with a single element. */
public static final String VALUE = "value"; private static final Map<AnnotationCacheKey, Annotation> findAnnotationCache = new ConcurrentReferenceHashMap<>(256); private static final Map<AnnotationCacheKey, Boolean> metaPresentCache = new ConcurrentReferenceHashMap<>(256); private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationsCache = new ConcurrentReferenceHashMap<>(256); private static final Map<Class<?>, Set<Method>> annotatedBaseTypeCache = new ConcurrentReferenceHashMap<>(256); @SuppressWarnings("unused") @Deprecated // just here for older tool versions trying to reflectively clear the cache private static final Map<Class<?>, ?> annotatedInterfaceCache = annotatedBaseTypeCache; private static final Map<Class<? extends Annotation>, Boolean> synthesizableCache = new ConcurrentReferenceHashMap<>(256); private static final Map<Class<? extends Annotation>, Map<String, List<String>>> attributeAliasesCache = new ConcurrentReferenceHashMap<>(256); private static final Map<Class<? extends Annotation>, List<Method>> attributeMethodsCache = new ConcurrentReferenceHashMap<>(256); private static final Map<Method, AliasDescriptor> aliasDescriptorCache = new ConcurrentReferenceHashMap<>(256); @Nullable private static transient Log logger;
Get a single Annotation of annotationType from the supplied annotation: either the given annotation itself or a direct meta-annotation thereof.

Note that this method supports only a single level of meta-annotations. For support for arbitrary levels of meta-annotations, use one of the find*() methods instead.

Params:
  • annotation – the Annotation to check
  • annotationType – the annotation type to look for, both locally and as a meta-annotation
Returns:the first matching annotation, or null if not found
Since:4.0
/** * Get a single {@link Annotation} of {@code annotationType} from the supplied * annotation: either the given annotation itself or a direct meta-annotation * thereof. * <p>Note that this method supports only a single level of meta-annotations. * For support for arbitrary levels of meta-annotations, use one of the * {@code find*()} methods instead. * @param annotation the Annotation to check * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @return the first matching annotation, or {@code null} if not found * @since 4.0 */
@SuppressWarnings("unchecked") @Nullable public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) { if (annotationType.isInstance(annotation)) { return synthesizeAnnotation((A) annotation); } Class<? extends Annotation> annotatedElement = annotation.annotationType(); try { A metaAnn = annotatedElement.getAnnotation(annotationType); return (metaAnn != null ? synthesizeAnnotation(metaAnn, annotatedElement) : null); } catch (Throwable ex) { handleIntrospectionFailure(annotatedElement, ex); return null; } }
Get a single Annotation of annotationType from the supplied AnnotatedElement, where the annotation is either present or meta-present on the AnnotatedElement.

Note that this method supports only a single level of meta-annotations. For support for arbitrary levels of meta-annotations, use findAnnotation(AnnotatedElement, Class<Annotation>) instead.

Params:
  • annotatedElement – the AnnotatedElement from which to get the annotation
  • annotationType – the annotation type to look for, both locally and as a meta-annotation
Returns:the first matching annotation, or null if not found
Since:3.1
/** * Get a single {@link Annotation} of {@code annotationType} from the supplied * {@link AnnotatedElement}, where the annotation is either <em>present</em> or * <em>meta-present</em> on the {@code AnnotatedElement}. * <p>Note that this method supports only a single level of meta-annotations. * For support for arbitrary levels of meta-annotations, use * {@link #findAnnotation(AnnotatedElement, Class)} instead. * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @return the first matching annotation, or {@code null} if not found * @since 3.1 */
@Nullable public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) { try { A annotation = annotatedElement.getAnnotation(annotationType); if (annotation == null) { for (Annotation metaAnn : annotatedElement.getAnnotations()) { annotation = metaAnn.annotationType().getAnnotation(annotationType); if (annotation != null) { break; } } } return (annotation != null ? synthesizeAnnotation(annotation, annotatedElement) : null); } catch (Throwable ex) { handleIntrospectionFailure(annotatedElement, ex); return null; } }
Get a single Annotation of annotationType from the supplied Method, where the annotation is either present or meta-present on the method.

Correctly handles bridge Methods generated by the compiler.

Note that this method supports only a single level of meta-annotations. For support for arbitrary levels of meta-annotations, use findAnnotation(Method, Class<Annotation>) instead.

Params:
  • method – the method to look for annotations on
  • annotationType – the annotation type to look for
See Also:
Returns:the first matching annotation, or null if not found
/** * Get a single {@link Annotation} of {@code annotationType} from the * supplied {@link Method}, where the annotation is either <em>present</em> * or <em>meta-present</em> on the method. * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. * <p>Note that this method supports only a single level of meta-annotations. * For support for arbitrary levels of meta-annotations, use * {@link #findAnnotation(Method, Class)} instead. * @param method the method to look for annotations on * @param annotationType the annotation type to look for * @return the first matching annotation, or {@code null} if not found * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) * @see #getAnnotation(AnnotatedElement, Class) */
@Nullable public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) { Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); return getAnnotation((AnnotatedElement) resolvedMethod, annotationType); }
Get all Annotations that are present on the supplied AnnotatedElement.

Meta-annotations will not be searched.

Params:
  • annotatedElement – the Method, Constructor or Field to retrieve annotations from
See Also:
Returns:the annotations found, an empty array, or null if not resolvable (e.g. because nested Class values in annotation attributes failed to resolve at runtime)
Since:4.0.8
/** * Get all {@link Annotation Annotations} that are <em>present</em> on the * supplied {@link AnnotatedElement}. * <p>Meta-annotations will <em>not</em> be searched. * @param annotatedElement the Method, Constructor or Field to retrieve annotations from * @return the annotations found, an empty array, or {@code null} if not * resolvable (e.g. because nested Class values in annotation attributes * failed to resolve at runtime) * @since 4.0.8 * @see AnnotatedElement#getAnnotations() */
@Nullable public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) { try { return synthesizeAnnotationArray(annotatedElement.getAnnotations(), annotatedElement); } catch (Throwable ex) { handleIntrospectionFailure(annotatedElement, ex); return null; } }
Get all Annotations that are present on the supplied Method.

Correctly handles bridge Methods generated by the compiler.

Meta-annotations will not be searched.

Params:
  • method – the Method to retrieve annotations from
See Also:
Returns:the annotations found, an empty array, or null if not resolvable (e.g. because nested Class values in annotation attributes failed to resolve at runtime)
/** * Get all {@link Annotation Annotations} that are <em>present</em> on the * supplied {@link Method}. * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. * <p>Meta-annotations will <em>not</em> be searched. * @param method the Method to retrieve annotations from * @return the annotations found, an empty array, or {@code null} if not * resolvable (e.g. because nested Class values in annotation attributes * failed to resolve at runtime) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) * @see AnnotatedElement#getAnnotations() */
@Nullable public static Annotation[] getAnnotations(Method method) { try { return synthesizeAnnotationArray(BridgeMethodResolver.findBridgedMethod(method).getAnnotations(), method); } catch (Throwable ex) { handleIntrospectionFailure(method, ex); return null; } }
Get the repeatable annotations of annotationType from the supplied AnnotatedElement, where such annotations are either present, indirectly present, or meta-present on the element.

This method mimics the functionality of Java 8's AnnotatedElement.getAnnotationsByType(Class<Annotation>) with support for automatic detection of a container annotation declared via @Repeatable (when running on Java 8 or higher) and with additional support for meta-annotations.

Handles both single annotations and annotations nested within a container annotation.

Correctly handles bridge methods generated by the compiler if the supplied element is a Method.

Meta-annotations will be searched if the annotation is not present on the supplied element.

Params:
  • annotatedElement – the element to look for annotations on
  • annotationType – the annotation type to look for
See Also:
Returns:the annotations found or an empty set (never null)
Since:4.2
/** * Get the <em>repeatable</em> {@linkplain Annotation annotations} of * {@code annotationType} from the supplied {@link AnnotatedElement}, where * such annotations are either <em>present</em>, <em>indirectly present</em>, * or <em>meta-present</em> on the element. * <p>This method mimics the functionality of Java 8's * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)} * with support for automatic detection of a <em>container annotation</em> * declared via @{@link java.lang.annotation.Repeatable} (when running on * Java 8 or higher) and with additional support for meta-annotations. * <p>Handles both single annotations and annotations nested within a * <em>container annotation</em>. * <p>Correctly handles <em>bridge methods</em> generated by the * compiler if the supplied element is a {@link Method}. * <p>Meta-annotations will be searched if the annotation is not * <em>present</em> on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType */
public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType) { return getRepeatableAnnotations(annotatedElement, annotationType, null); }
Get the repeatable annotations of annotationType from the supplied AnnotatedElement, where such annotations are either present, indirectly present, or meta-present on the element.

This method mimics the functionality of Java 8's AnnotatedElement.getAnnotationsByType(Class<Annotation>) with additional support for meta-annotations.

Handles both single annotations and annotations nested within a container annotation.

Correctly handles bridge methods generated by the compiler if the supplied element is a Method.

Meta-annotations will be searched if the annotation is not present on the supplied element.

Params:
  • annotatedElement – the element to look for annotations on
  • annotationType – the annotation type to look for
  • containerAnnotationType – the type of the container that holds the annotations; may be null if a container is not supported or if it should be looked up via @Repeatable when running on Java 8 or higher
See Also:
Returns:the annotations found or an empty set (never null)
Since:4.2
/** * Get the <em>repeatable</em> {@linkplain Annotation annotations} of * {@code annotationType} from the supplied {@link AnnotatedElement}, where * such annotations are either <em>present</em>, <em>indirectly present</em>, * or <em>meta-present</em> on the element. * <p>This method mimics the functionality of Java 8's * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)} * with additional support for meta-annotations. * <p>Handles both single annotations and annotations nested within a * <em>container annotation</em>. * <p>Correctly handles <em>bridge methods</em> generated by the * compiler if the supplied element is a {@link Method}. * <p>Meta-annotations will be searched if the annotation is not * <em>present</em> on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @param containerAnnotationType the type of the container that holds * the annotations; may be {@code null} if a container is not supported * or if it should be looked up via @{@link java.lang.annotation.Repeatable} * when running on Java 8 or higher * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see #getRepeatableAnnotations(AnnotatedElement, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType */
public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) { Set<A> annotations = getDeclaredRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType); if (annotations.isEmpty() && annotatedElement instanceof Class) { Class<?> superclass = ((Class<?>) annotatedElement).getSuperclass(); if (superclass != null && superclass != Object.class) { return getRepeatableAnnotations(superclass, annotationType, containerAnnotationType); } } return annotations; }
Get the declared repeatable annotations of annotationType from the supplied AnnotatedElement, where such annotations are either directly present, indirectly present, or meta-present on the element.

This method mimics the functionality of Java 8's AnnotatedElement.getDeclaredAnnotationsByType(Class<Annotation>) with support for automatic detection of a container annotation declared via @Repeatable (when running on Java 8 or higher) and with additional support for meta-annotations.

Handles both single annotations and annotations nested within a container annotation.

Correctly handles bridge methods generated by the compiler if the supplied element is a Method.

Meta-annotations will be searched if the annotation is not present on the supplied element.

Params:
  • annotatedElement – the element to look for annotations on
  • annotationType – the annotation type to look for
See Also:
Returns:the annotations found or an empty set (never null)
Since:4.2
/** * Get the declared <em>repeatable</em> {@linkplain Annotation annotations} * of {@code annotationType} from the supplied {@link AnnotatedElement}, * where such annotations are either <em>directly present</em>, * <em>indirectly present</em>, or <em>meta-present</em> on the element. * <p>This method mimics the functionality of Java 8's * {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)} * with support for automatic detection of a <em>container annotation</em> * declared via @{@link java.lang.annotation.Repeatable} (when running on * Java 8 or higher) and with additional support for meta-annotations. * <p>Handles both single annotations and annotations nested within a * <em>container annotation</em>. * <p>Correctly handles <em>bridge methods</em> generated by the * compiler if the supplied element is a {@link Method}. * <p>Meta-annotations will be searched if the annotation is not * <em>present</em> on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see #getRepeatableAnnotations(AnnotatedElement, Class) * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType */
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType) { return getDeclaredRepeatableAnnotations(annotatedElement, annotationType, null); }
Get the declared repeatable annotations of annotationType from the supplied AnnotatedElement, where such annotations are either directly present, indirectly present, or meta-present on the element.

This method mimics the functionality of Java 8's AnnotatedElement.getDeclaredAnnotationsByType(Class<Annotation>) with additional support for meta-annotations.

Handles both single annotations and annotations nested within a container annotation.

Correctly handles bridge methods generated by the compiler if the supplied element is a Method.

Meta-annotations will be searched if the annotation is not present on the supplied element.

Params:
  • annotatedElement – the element to look for annotations on
  • annotationType – the annotation type to look for
  • containerAnnotationType – the type of the container that holds the annotations; may be null if a container is not supported or if it should be looked up via @Repeatable when running on Java 8 or higher
See Also:
Returns:the annotations found or an empty set (never null)
Since:4.2
/** * Get the declared <em>repeatable</em> {@linkplain Annotation annotations} * of {@code annotationType} from the supplied {@link AnnotatedElement}, * where such annotations are either <em>directly present</em>, * <em>indirectly present</em>, or <em>meta-present</em> on the element. * <p>This method mimics the functionality of Java 8's * {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)} * with additional support for meta-annotations. * <p>Handles both single annotations and annotations nested within a * <em>container annotation</em>. * <p>Correctly handles <em>bridge methods</em> generated by the * compiler if the supplied element is a {@link Method}. * <p>Meta-annotations will be searched if the annotation is not * <em>present</em> on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @param containerAnnotationType the type of the container that holds * the annotations; may be {@code null} if a container is not supported * or if it should be looked up via @{@link java.lang.annotation.Repeatable} * when running on Java 8 or higher * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see #getRepeatableAnnotations(AnnotatedElement, Class) * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class) * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType */
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) { try { if (annotatedElement instanceof Method) { annotatedElement = BridgeMethodResolver.findBridgedMethod((Method) annotatedElement); } return new AnnotationCollector<>(annotationType, containerAnnotationType).getResult(annotatedElement); } catch (Throwable ex) { handleIntrospectionFailure(annotatedElement, ex); return Collections.emptySet(); } }
Find a single Annotation of annotationType on the supplied AnnotatedElement.

Meta-annotations will be searched if the annotation is not directly present on the supplied element.

Warning: this method operates generically on annotated elements. In other words, this method does not execute specialized search algorithms for classes or methods. If you require the more specific semantics of findAnnotation(Class<?>, Class<Annotation>) or findAnnotation(Method, Class<Annotation>), invoke one of those methods instead.

Params:
  • annotatedElement – the AnnotatedElement on which to find the annotation
  • annotationType – the annotation type to look for, both locally and as a meta-annotation
Returns:the first matching annotation, or null if not found
Since:4.2
/** * Find a single {@link Annotation} of {@code annotationType} on the * supplied {@link AnnotatedElement}. * <p>Meta-annotations will be searched if the annotation is not * <em>directly present</em> on the supplied element. * <p><strong>Warning</strong>: this method operates generically on * annotated elements. In other words, this method does not execute * specialized search algorithms for classes or methods. If you require * the more specific semantics of {@link #findAnnotation(Class, Class)} * or {@link #findAnnotation(Method, Class)}, invoke one of those methods * instead. * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @return the first matching annotation, or {@code null} if not found * @since 4.2 */
@Nullable public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) { // Do NOT store result in the findAnnotationCache since doing so could break // findAnnotation(Class, Class) and findAnnotation(Method, Class). A ann = findAnnotation(annotatedElement, annotationType, new HashSet<>()); return (ann != null ? synthesizeAnnotation(ann, annotatedElement) : null); }
Perform the search algorithm for findAnnotation(AnnotatedElement, Class<Annotation>) avoiding endless recursion by tracking which annotations have already been visited.
Params:
  • annotatedElement – the AnnotatedElement on which to find the annotation
  • annotationType – the annotation type to look for, both locally and as a meta-annotation
  • visited – the set of annotations that have already been visited
Returns:the first matching annotation, or null if not found
Since:4.2
/** * Perform the search algorithm for {@link #findAnnotation(AnnotatedElement, Class)} * avoiding endless recursion by tracking which annotations have already * been <em>visited</em>. * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @param visited the set of annotations that have already been visited * @return the first matching annotation, or {@code null} if not found * @since 4.2 */
@Nullable private static <A extends Annotation> A findAnnotation( AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) { try { A annotation = annotatedElement.getDeclaredAnnotation(annotationType); if (annotation != null) { return annotation; } for (Annotation declaredAnn : getDeclaredAnnotations(annotatedElement)) { Class<? extends Annotation> declaredType = declaredAnn.annotationType(); if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) { annotation = findAnnotation((AnnotatedElement) declaredType, annotationType, visited); if (annotation != null) { return annotation; } } } } catch (Throwable ex) { handleIntrospectionFailure(annotatedElement, ex); } return null; }
Find a single Annotation of annotationType on the supplied Method, traversing its super methods (i.e. from superclasses and interfaces) if the annotation is not directly present on the given method itself.

Correctly handles bridge Methods generated by the compiler.

Meta-annotations will be searched if the annotation is not directly present on the method.

Annotations on methods are not inherited by default, so we need to handle this explicitly.

Params:
  • method – the method to look for annotations on
  • annotationType – the annotation type to look for
See Also:
Returns:the first matching annotation, or null if not found
/** * Find a single {@link Annotation} of {@code annotationType} on the supplied * {@link Method}, traversing its super methods (i.e. from superclasses and * interfaces) if the annotation is not <em>directly present</em> on the given * method itself. * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. * <p>Meta-annotations will be searched if the annotation is not * <em>directly present</em> on the method. * <p>Annotations on methods are not inherited by default, so we need to handle * this explicitly. * @param method the method to look for annotations on * @param annotationType the annotation type to look for * @return the first matching annotation, or {@code null} if not found * @see #getAnnotation(Method, Class) */
@SuppressWarnings("unchecked") @Nullable public static <A extends Annotation> A findAnnotation(Method method, @Nullable Class<A> annotationType) { Assert.notNull(method, "Method must not be null"); if (annotationType == null) { return null; } AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType); A result = (A) findAnnotationCache.get(cacheKey); if (result == null) { Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType); if (result == null) { result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces()); } Class<?> clazz = method.getDeclaringClass(); while (result == null) { clazz = clazz.getSuperclass(); if (clazz == null || clazz == Object.class) { break; } Set<Method> annotatedMethods = getAnnotatedMethodsInBaseType(clazz); if (!annotatedMethods.isEmpty()) { for (Method annotatedMethod : annotatedMethods) { if (isOverride(method, annotatedMethod)) { Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod); result = findAnnotation((AnnotatedElement) resolvedSuperMethod, annotationType); if (result != null) { break; } } } } if (result == null) { result = searchOnInterfaces(method, annotationType, clazz.getInterfaces()); } } if (result != null) { result = synthesizeAnnotation(result, method); findAnnotationCache.put(cacheKey, result); } } return result; } @Nullable private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) { for (Class<?> ifc : ifcs) { Set<Method> annotatedMethods = getAnnotatedMethodsInBaseType(ifc); if (!annotatedMethods.isEmpty()) { for (Method annotatedMethod : annotatedMethods) { if (isOverride(method, annotatedMethod)) { A annotation = getAnnotation(annotatedMethod, annotationType); if (annotation != null) { return annotation; } } } } } return null; }
Does the given method override the given candidate method?
Params:
  • method – the overriding method
  • candidate – the potentially overridden method
Since:5.0.8
/** * Does the given method override the given candidate method? * @param method the overriding method * @param candidate the potentially overridden method * @since 5.0.8 */
static boolean isOverride(Method method, Method candidate) { if (!candidate.getName().equals(method.getName()) || candidate.getParameterCount() != method.getParameterCount()) { return false; } Class<?>[] paramTypes = method.getParameterTypes(); if (Arrays.equals(candidate.getParameterTypes(), paramTypes)) { return true; } for (int i = 0; i < paramTypes.length; i++) { if (paramTypes[i] != ResolvableType.forMethodParameter(candidate, i, method.getDeclaringClass()).resolve()) { return false; } } return true; }
Determine the methods on the given type with searchable annotations on them.
Params:
  • baseType – the superclass or interface to search
Returns:the cached set of annotated methods
Since:5.0.5
/** * Determine the methods on the given type with searchable annotations on them. * @param baseType the superclass or interface to search * @return the cached set of annotated methods * @since 5.0.5 */
static Set<Method> getAnnotatedMethodsInBaseType(Class<?> baseType) { boolean ifcCheck = baseType.isInterface(); if (ifcCheck && ClassUtils.isJavaLanguageInterface(baseType)) { return Collections.emptySet(); } Set<Method> annotatedMethods = annotatedBaseTypeCache.get(baseType); if (annotatedMethods != null) { return annotatedMethods; } Method[] methods = (ifcCheck ? baseType.getMethods() : baseType.getDeclaredMethods()); for (Method baseMethod : methods) { try { // Public methods on interfaces (including interface hierarchy), // non-private (and therefore overridable) methods on base classes if ((ifcCheck || !Modifier.isPrivate(baseMethod.getModifiers())) && hasSearchableAnnotations(baseMethod)) { if (annotatedMethods == null) { annotatedMethods = new HashSet<>(); } annotatedMethods.add(baseMethod); } } catch (Throwable ex) { handleIntrospectionFailure(baseMethod, ex); } } if (annotatedMethods == null) { annotatedMethods = Collections.emptySet(); } annotatedBaseTypeCache.put(baseType, annotatedMethods); return annotatedMethods; }
Determine whether the specified method has searchable annotations, i.e. not just java.lang or org.springframework.lang annotations such as Deprecated and Nullable.
Params:
  • ifcMethod – the interface method to check
@@since5.0.5
/** * Determine whether the specified method has searchable annotations, * i.e. not just {@code java.lang} or {@code org.springframework.lang} * annotations such as {@link Deprecated} and {@link Nullable}. * @param ifcMethod the interface method to check * @@since 5.0.5 */
private static boolean hasSearchableAnnotations(Method ifcMethod) { Annotation[] anns = getDeclaredAnnotations(ifcMethod); if (anns.length == 0) { return false; } for (Annotation ann : anns) { String name = ann.annotationType().getName(); if (!name.startsWith("java.lang.") && !name.startsWith("org.springframework.lang.")) { return true; } } return false; }
Retrieve a potentially cached array of declared annotations for the given element.
Params:
  • element – the annotated element to introspect
Returns:a potentially cached array of declared annotations (only for internal iteration purposes, not for external exposure)
Since:5.1
/** * Retrieve a potentially cached array of declared annotations for the * given element. * @param element the annotated element to introspect * @return a potentially cached array of declared annotations * (only for internal iteration purposes, not for external exposure) * @since 5.1 */
static Annotation[] getDeclaredAnnotations(AnnotatedElement element) { if (element instanceof Class || element instanceof Member) { // Class/Field/Method/Constructor returns a defensively cloned array from getDeclaredAnnotations. // Since we use our result for internal iteration purposes only, it's safe to use a shared copy. return declaredAnnotationsCache.computeIfAbsent(element, AnnotatedElement::getDeclaredAnnotations); } return element.getDeclaredAnnotations(); }
Find a single Annotation of annotationType on the supplied Class, traversing its interfaces, annotations, and superclasses if the annotation is not directly present on the given class itself.

This method explicitly handles class-level annotations which are not declared as inherited as well as meta-annotations and annotations on interfaces.

The algorithm operates as follows:

  1. Search for the annotation on the given class and return it if found.
  2. Recursively search through all annotations that the given class declares.
  3. Recursively search through all interfaces that the given class declares.
  4. Recursively search through the superclass hierarchy of the given class.

Note: in this context, the term recursively means that the search process continues by returning to step #1 with the current interface, annotation, or superclass as the class to look for annotations on.

Params:
  • clazz – the class to look for annotations on
  • annotationType – the type of annotation to look for
Returns:the first matching annotation, or null if not found
/** * Find a single {@link Annotation} of {@code annotationType} on the * supplied {@link Class}, traversing its interfaces, annotations, and * superclasses if the annotation is not <em>directly present</em> on * the given class itself. * <p>This method explicitly handles class-level annotations which are not * declared as {@link java.lang.annotation.Inherited inherited} <em>as well * as meta-annotations and annotations on interfaces</em>. * <p>The algorithm operates as follows: * <ol> * <li>Search for the annotation on the given class and return it if found. * <li>Recursively search through all annotations that the given class declares. * <li>Recursively search through all interfaces that the given class declares. * <li>Recursively search through the superclass hierarchy of the given class. * </ol> * <p>Note: in this context, the term <em>recursively</em> means that the search * process continues by returning to step #1 with the current interface, * annotation, or superclass as the class to look for annotations on. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @return the first matching annotation, or {@code null} if not found */
@Nullable public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) { return findAnnotation(clazz, annotationType, true); }
Perform the actual work for findAnnotation(AnnotatedElement, Class<Annotation>), honoring the synthesize flag.
Params:
  • clazz – the class to look for annotations on
  • annotationType – the type of annotation to look for
  • synthesize – true if the result should be synthesized
Returns:the first matching annotation, or null if not found
Since:4.2.1
/** * Perform the actual work for {@link #findAnnotation(AnnotatedElement, Class)}, * honoring the {@code synthesize} flag. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @param synthesize {@code true} if the result should be * {@linkplain #synthesizeAnnotation(Annotation) synthesized} * @return the first matching annotation, or {@code null} if not found * @since 4.2.1 */
@SuppressWarnings("unchecked") @Nullable private static <A extends Annotation> A findAnnotation( Class<?> clazz, @Nullable Class<A> annotationType, boolean synthesize) { Assert.notNull(clazz, "Class must not be null"); if (annotationType == null) { return null; } AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType); A result = (A) findAnnotationCache.get(cacheKey); if (result == null) { result = findAnnotation(clazz, annotationType, new HashSet<>()); if (result != null && synthesize) { result = synthesizeAnnotation(result, clazz); findAnnotationCache.put(cacheKey, result); } } return result; }
Perform the search algorithm for findAnnotation(Class<?>, Class<Annotation>), avoiding endless recursion by tracking which annotations have already been visited.
Params:
  • clazz – the class to look for annotations on
  • annotationType – the type of annotation to look for
  • visited – the set of annotations that have already been visited
Returns:the first matching annotation, or null if not found
/** * Perform the search algorithm for {@link #findAnnotation(Class, Class)}, * avoiding endless recursion by tracking which annotations have already * been <em>visited</em>. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @param visited the set of annotations that have already been visited * @return the first matching annotation, or {@code null} if not found */
@Nullable private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) { try { A annotation = clazz.getDeclaredAnnotation(annotationType); if (annotation != null) { return annotation; } for (Annotation declaredAnn : getDeclaredAnnotations(clazz)) { Class<? extends Annotation> declaredType = declaredAnn.annotationType(); if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) { annotation = findAnnotation(declaredType, annotationType, visited); if (annotation != null) { return annotation; } } } } catch (Throwable ex) { handleIntrospectionFailure(clazz, ex); return null; } for (Class<?> ifc : clazz.getInterfaces()) { A annotation = findAnnotation(ifc, annotationType, visited); if (annotation != null) { return annotation; } } Class<?> superclass = clazz.getSuperclass(); if (superclass == null || superclass == Object.class) { return null; } return findAnnotation(superclass, annotationType, visited); }
Find the first Class in the inheritance hierarchy of the specified clazz (including the specified clazz itself) on which an annotation of the specified annotationType is directly present.

If the supplied clazz is an interface, only the interface itself will be checked; the inheritance hierarchy for interfaces will not be traversed.

Meta-annotations will not be searched.

The standard Class API does not provide a mechanism for determining which class in an inheritance hierarchy actually declares an Annotation, so we need to handle this explicitly.

Params:
  • annotationType – the annotation type to look for
  • clazz – the class to check for the annotation on (may be null)
See Also:
Returns:the first Class in the inheritance hierarchy that declares an annotation of the specified annotationType, or null if not found
/** * Find the first {@link Class} in the inheritance hierarchy of the * specified {@code clazz} (including the specified {@code clazz} itself) * on which an annotation of the specified {@code annotationType} is * <em>directly present</em>. * <p>If the supplied {@code clazz} is an interface, only the interface * itself will be checked; the inheritance hierarchy for interfaces will * not be traversed. * <p>Meta-annotations will <em>not</em> be searched. * <p>The standard {@link Class} API does not provide a mechanism for * determining which class in an inheritance hierarchy actually declares * an {@link Annotation}, so we need to handle this explicitly. * @param annotationType the annotation type to look for * @param clazz the class to check for the annotation on (may be {@code null}) * @return the first {@link Class} in the inheritance hierarchy that * declares an annotation of the specified {@code annotationType}, or * {@code null} if not found * @see Class#isAnnotationPresent(Class) * @see Class#getDeclaredAnnotations() * @see #findAnnotationDeclaringClassForTypes(List, Class) * @see #isAnnotationDeclaredLocally(Class, Class) */
@Nullable public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, @Nullable Class<?> clazz) { if (clazz == null || clazz == Object.class) { return null; } if (isAnnotationDeclaredLocally(annotationType, clazz)) { return clazz; } return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass()); }
Find the first Class in the inheritance hierarchy of the specified clazz (including the specified clazz itself) on which at least one of the specified annotationTypes is directly present.

If the supplied clazz is an interface, only the interface itself will be checked; the inheritance hierarchy for interfaces will not be traversed.

Meta-annotations will not be searched.

The standard Class API does not provide a mechanism for determining which class in an inheritance hierarchy actually declares one of several candidate annotations, so we need to handle this explicitly.

Params:
  • annotationTypes – the annotation types to look for
  • clazz – the class to check for the annotations on, or null
See Also:
Returns:the first Class in the inheritance hierarchy that declares an annotation of at least one of the specified annotationTypes, or null if not found
Since:3.2.2
/** * Find the first {@link Class} in the inheritance hierarchy of the * specified {@code clazz} (including the specified {@code clazz} itself) * on which at least one of the specified {@code annotationTypes} is * <em>directly present</em>. * <p>If the supplied {@code clazz} is an interface, only the interface * itself will be checked; the inheritance hierarchy for interfaces will * not be traversed. * <p>Meta-annotations will <em>not</em> be searched. * <p>The standard {@link Class} API does not provide a mechanism for * determining which class in an inheritance hierarchy actually declares * one of several candidate {@linkplain Annotation annotations}, so we * need to handle this explicitly. * @param annotationTypes the annotation types to look for * @param clazz the class to check for the annotations on, or {@code null} * @return the first {@link Class} in the inheritance hierarchy that * declares an annotation of at least one of the specified * {@code annotationTypes}, or {@code null} if not found * @since 3.2.2 * @see Class#isAnnotationPresent(Class) * @see Class#getDeclaredAnnotations() * @see #findAnnotationDeclaringClass(Class, Class) * @see #isAnnotationDeclaredLocally(Class, Class) */
@Nullable public static Class<?> findAnnotationDeclaringClassForTypes( List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) { if (clazz == null || clazz == Object.class) { return null; } for (Class<? extends Annotation> annotationType : annotationTypes) { if (isAnnotationDeclaredLocally(annotationType, clazz)) { return clazz; } } return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass()); }
Determine whether an annotation of the specified annotationType is declared locally (i.e. directly present) on the supplied clazz.

The supplied Class may represent any type.

Meta-annotations will not be searched.

Note: This method does not determine if the annotation is inherited. For greater clarity regarding inherited annotations, consider using isAnnotationInherited(Class<? extends Annotation>, Class<?>) instead.

Params:
  • annotationType – the annotation type to look for
  • clazz – the class to check for the annotation on
See Also:
Returns:true if an annotation of the specified annotationType is directly present
/** * Determine whether an annotation of the specified {@code annotationType} * is declared locally (i.e. <em>directly present</em>) on the supplied * {@code clazz}. * <p>The supplied {@link Class} may represent any type. * <p>Meta-annotations will <em>not</em> be searched. * <p>Note: This method does <strong>not</strong> determine if the annotation * is {@linkplain java.lang.annotation.Inherited inherited}. For greater * clarity regarding inherited annotations, consider using * {@link #isAnnotationInherited(Class, Class)} instead. * @param annotationType the annotation type to look for * @param clazz the class to check for the annotation on * @return {@code true} if an annotation of the specified {@code annotationType} * is <em>directly present</em> * @see java.lang.Class#getDeclaredAnnotations() * @see java.lang.Class#getDeclaredAnnotation(Class) * @see #isAnnotationInherited(Class, Class) */
public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) { try { return (clazz.getDeclaredAnnotation(annotationType) != null); } catch (Throwable ex) { handleIntrospectionFailure(clazz, ex); return false; } }
Determine whether an annotation of the specified annotationType is present on the supplied clazz and is inherited (i.e. not directly present).

Meta-annotations will not be searched.

If the supplied clazz is an interface, only the interface itself will be checked. In accordance with standard meta-annotation semantics in Java, the inheritance hierarchy for interfaces will not be traversed. See the javadoc for the @Inherited meta-annotation for further details regarding annotation inheritance.

Params:
  • annotationType – the annotation type to look for
  • clazz – the class to check for the annotation on
See Also:
Returns:true if an annotation of the specified annotationType is present and inherited
/** * Determine whether an annotation of the specified {@code annotationType} * is <em>present</em> on the supplied {@code clazz} and is * {@linkplain java.lang.annotation.Inherited inherited} * (i.e. not <em>directly present</em>). * <p>Meta-annotations will <em>not</em> be searched. * <p>If the supplied {@code clazz} is an interface, only the interface * itself will be checked. In accordance with standard meta-annotation * semantics in Java, the inheritance hierarchy for interfaces will not * be traversed. See the {@linkplain java.lang.annotation.Inherited javadoc} * for the {@code @Inherited} meta-annotation for further details regarding * annotation inheritance. * @param annotationType the annotation type to look for * @param clazz the class to check for the annotation on * @return {@code true} if an annotation of the specified {@code annotationType} * is <em>present</em> and <em>inherited</em> * @see Class#isAnnotationPresent(Class) * @see #isAnnotationDeclaredLocally(Class, Class) */
public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) { return (clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz)); }
Determine if an annotation of type metaAnnotationType is meta-present on the supplied annotationType.
Params:
  • annotationType – the annotation type to search on
  • metaAnnotationType – the type of meta-annotation to search for
Returns:true if such an annotation is meta-present
Since:4.2.1
/** * Determine if an annotation of type {@code metaAnnotationType} is * <em>meta-present</em> on the supplied {@code annotationType}. * @param annotationType the annotation type to search on * @param metaAnnotationType the type of meta-annotation to search for * @return {@code true} if such an annotation is meta-present * @since 4.2.1 */
public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType, @Nullable Class<? extends Annotation> metaAnnotationType) { Assert.notNull(annotationType, "Annotation type must not be null"); if (metaAnnotationType == null) { return false; } AnnotationCacheKey cacheKey = new AnnotationCacheKey(annotationType, metaAnnotationType); Boolean metaPresent = metaPresentCache.get(cacheKey); if (metaPresent != null) { return metaPresent; } metaPresent = Boolean.FALSE; if (findAnnotation(annotationType, metaAnnotationType, false) != null) { metaPresent = Boolean.TRUE; } metaPresentCache.put(cacheKey, metaPresent); return metaPresent; }
Determine if the given annotated element is defined in a java or in the org.springframework.lang package.
Params:
  • annotatedElement – the annotated element to check
Returns:true if the given element is in a java package or in the org.springframework.lang package
Since:5.1
/** * Determine if the given annotated element is defined in a * {@code java} or in the {@code org.springframework.lang} package. * @param annotatedElement the annotated element to check * @return {@code true} if the given element is in a {@code java} * package or in the {@code org.springframework.lang} package * @since 5.1 */
static boolean hasPlainJavaAnnotationsOnly(@Nullable Object annotatedElement) { Class<?> clazz; if (annotatedElement instanceof Class) { clazz = (Class<?>) annotatedElement; } else if (annotatedElement instanceof Member) { clazz = ((Member) annotatedElement).getDeclaringClass(); } else { return false; } String name = clazz.getName(); return (name.startsWith("java") || name.startsWith("org.springframework.lang.")); }
Determine if the supplied Annotation is defined in the core JDK java.lang.annotation package.
Params:
  • annotation – the annotation to check
Returns:true if the annotation is in the java.lang.annotation package
/** * Determine if the supplied {@link Annotation} is defined in the core JDK * {@code java.lang.annotation} package. * @param annotation the annotation to check * @return {@code true} if the annotation is in the {@code java.lang.annotation} package */
public static boolean isInJavaLangAnnotationPackage(@Nullable Annotation annotation) { return (annotation != null && isInJavaLangAnnotationPackage(annotation.annotationType())); }
Determine if the Annotation with the supplied name is defined in the core JDK java.lang.annotation package.
Params:
  • annotationType – the annotation type to check
Returns:true if the annotation is in the java.lang.annotation package
Since:4.3.8
/** * Determine if the {@link Annotation} with the supplied name is defined * in the core JDK {@code java.lang.annotation} package. * @param annotationType the annotation type to check * @return {@code true} if the annotation is in the {@code java.lang.annotation} package * @since 4.3.8 */
static boolean isInJavaLangAnnotationPackage(@Nullable Class<? extends Annotation> annotationType) { return (annotationType != null && isInJavaLangAnnotationPackage(annotationType.getName())); }
Determine if the Annotation with the supplied name is defined in the core JDK java.lang.annotation package.
Params:
  • annotationType – the name of the annotation type to check
Returns:true if the annotation is in the java.lang.annotation package
Since:4.2
/** * Determine if the {@link Annotation} with the supplied name is defined * in the core JDK {@code java.lang.annotation} package. * @param annotationType the name of the annotation type to check * @return {@code true} if the annotation is in the {@code java.lang.annotation} package * @since 4.2 */
public static boolean isInJavaLangAnnotationPackage(@Nullable String annotationType) { return (annotationType != null && annotationType.startsWith("java.lang.annotation")); }
Check the declared attributes of the given annotation, in particular covering Google App Engine's late arrival of TypeNotPresentExceptionProxy for Class values (instead of early Class.getAnnotations() failure.

This method not failing indicates that getAnnotationAttributes(Annotation) won't failure either (when attempted later on).

Params:
  • annotation – the annotation to validate
Throws:
See Also:
Since:4.3.15
/** * Check the declared attributes of the given annotation, in particular covering * Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for * {@code Class} values (instead of early {@code Class.getAnnotations() failure}. * <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)} * won't failure either (when attempted later on). * @param annotation the annotation to validate * @throws IllegalStateException if a declared {@code Class} attribute could not be read * @since 4.3.15 * @see Class#getAnnotations() * @see #getAnnotationAttributes(Annotation) */
public static void validateAnnotation(Annotation annotation) { for (Method method : getAttributeMethods(annotation.annotationType())) { Class<?> returnType = method.getReturnType(); if (returnType == Class.class || returnType == Class[].class) { try { method.invoke(annotation); } catch (Throwable ex) { throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex); } } } }
Retrieve the given annotation's attributes as a Map, preserving all attribute types.

Equivalent to calling getAnnotationAttributes(Annotation, boolean, boolean) with the classValuesAsString and nestedAnnotationsAsMap parameters set to false.

Note: This method actually returns an AnnotationAttributes instance. However, the Map signature has been preserved for binary compatibility.

Params:
  • annotation – the annotation to retrieve the attributes for
See Also:
Returns:the Map of annotation attributes, with attribute names as keys and corresponding attribute values as values (never null)
/** * Retrieve the given annotation's attributes as a {@link Map}, preserving all * attribute types. * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters * set to {@code false}. * <p>Note: This method actually returns an {@link AnnotationAttributes} instance. * However, the {@code Map} signature has been preserved for binary compatibility. * @param annotation the annotation to retrieve the attributes for * @return the Map of annotation attributes, with attribute names as keys and * corresponding attribute values as values (never {@code null}) * @see #getAnnotationAttributes(AnnotatedElement, Annotation) * @see #getAnnotationAttributes(Annotation, boolean, boolean) * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) */
public static Map<String, Object> getAnnotationAttributes(Annotation annotation) { return getAnnotationAttributes(null, annotation); }
Retrieve the given annotation's attributes as a Map.

Equivalent to calling getAnnotationAttributes(Annotation, boolean, boolean) with the nestedAnnotationsAsMap parameter set to false.

Note: This method actually returns an AnnotationAttributes instance. However, the Map signature has been preserved for binary compatibility.

Params:
  • annotation – the annotation to retrieve the attributes for
  • classValuesAsString – whether to convert Class references into Strings (for compatibility with AnnotationMetadata) or to preserve them as Class references
See Also:
Returns:the Map of annotation attributes, with attribute names as keys and corresponding attribute values as values (never null)
/** * Retrieve the given annotation's attributes as a {@link Map}. * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} * with the {@code nestedAnnotationsAsMap} parameter set to {@code false}. * <p>Note: This method actually returns an {@link AnnotationAttributes} instance. * However, the {@code Map} signature has been preserved for binary compatibility. * @param annotation the annotation to retrieve the attributes for * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @return the Map of annotation attributes, with attribute names as keys and * corresponding attribute values as values (never {@code null}) * @see #getAnnotationAttributes(Annotation, boolean, boolean) */
public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) { return getAnnotationAttributes(annotation, classValuesAsString, false); }
Retrieve the given annotation's attributes as an AnnotationAttributes map.

This method provides fully recursive annotation reading capabilities on par with the reflection-based StandardAnnotationMetadata.

Params:
  • annotation – the annotation to retrieve the attributes for
  • classValuesAsString – whether to convert Class references into Strings (for compatibility with AnnotationMetadata) or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested annotations into AnnotationAttributes maps (for compatibility with AnnotationMetadata) or to preserve them as Annotation instances
Returns:the annotation attributes (a specialized Map) with attribute names as keys and corresponding attribute values as values (never null)
Since:3.1.1
/** * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. * <p>This method provides fully recursive annotation reading capabilities on par with * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. * @param annotation the annotation to retrieve the attributes for * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested annotations into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @return the annotation attributes (a specialized Map) with attribute names as keys * and corresponding attribute values as values (never {@code null}) * @since 3.1.1 */
public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { return getAnnotationAttributes(null, annotation, classValuesAsString, nestedAnnotationsAsMap); }
Retrieve the given annotation's attributes as an AnnotationAttributes map.

Equivalent to calling getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) with the classValuesAsString and nestedAnnotationsAsMap parameters set to false.

Params:
  • annotatedElement – the element that is annotated with the supplied annotation; may be null if unknown
  • annotation – the annotation to retrieve the attributes for
See Also:
Returns:the annotation attributes (a specialized Map) with attribute names as keys and corresponding attribute values as values (never null)
Since:4.2
/** * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. * <p>Equivalent to calling {@link #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)} * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters * set to {@code false}. * @param annotatedElement the element that is annotated with the supplied annotation; * may be {@code null} if unknown * @param annotation the annotation to retrieve the attributes for * @return the annotation attributes (a specialized Map) with attribute names as keys * and corresponding attribute values as values (never {@code null}) * @since 4.2 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) */
public static AnnotationAttributes getAnnotationAttributes(@Nullable AnnotatedElement annotatedElement, Annotation annotation) { return getAnnotationAttributes(annotatedElement, annotation, false, false); }
Retrieve the given annotation's attributes as an AnnotationAttributes map.

This method provides fully recursive annotation reading capabilities on par with the reflection-based StandardAnnotationMetadata.

Params:
  • annotatedElement – the element that is annotated with the supplied annotation; may be null if unknown
  • annotation – the annotation to retrieve the attributes for
  • classValuesAsString – whether to convert Class references into Strings (for compatibility with AnnotationMetadata) or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested annotations into AnnotationAttributes maps (for compatibility with AnnotationMetadata) or to preserve them as Annotation instances
Returns:the annotation attributes (a specialized Map) with attribute names as keys and corresponding attribute values as values (never null)
Since:4.2
/** * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. * <p>This method provides fully recursive annotation reading capabilities on par with * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. * @param annotatedElement the element that is annotated with the supplied annotation; * may be {@code null} if unknown * @param annotation the annotation to retrieve the attributes for * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested annotations into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @return the annotation attributes (a specialized Map) with attribute names as keys * and corresponding attribute values as values (never {@code null}) * @since 4.2 */
public static AnnotationAttributes getAnnotationAttributes(@Nullable AnnotatedElement annotatedElement, Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { return getAnnotationAttributes( (Object) annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap); } private static AnnotationAttributes getAnnotationAttributes(@Nullable Object annotatedElement, Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { AnnotationAttributes attributes = retrieveAnnotationAttributes(annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap); postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, nestedAnnotationsAsMap); return attributes; }
Retrieve the given annotation's attributes as an AnnotationAttributes map.

This method provides fully recursive annotation reading capabilities on par with the reflection-based StandardAnnotationMetadata.

NOTE: This variant of getAnnotationAttributes() is only intended for use within the framework. The following special rules apply:

  1. Default values will be replaced with default value placeholders.
  2. The resulting, merged annotation attributes should eventually be post-processed in order to ensure that placeholders have been replaced by actual default values and in order to enforce @AliasFor semantics.
Params:
  • annotatedElement – the element that is annotated with the supplied annotation; may be null if unknown
  • annotation – the annotation to retrieve the attributes for
  • classValuesAsString – whether to convert Class references into Strings (for compatibility with AnnotationMetadata) or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested annotations into AnnotationAttributes maps (for compatibility with AnnotationMetadata) or to preserve them as Annotation instances
See Also:
Returns:the annotation attributes (a specialized Map) with attribute names as keys and corresponding attribute values as values (never null)
Since:4.2
/** * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. * <p>This method provides fully recursive annotation reading capabilities on par with * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. * <p><strong>NOTE</strong>: This variant of {@code getAnnotationAttributes()} is * only intended for use within the framework. The following special rules apply: * <ol> * <li>Default values will be replaced with default value placeholders.</li> * <li>The resulting, merged annotation attributes should eventually be * {@linkplain #postProcessAnnotationAttributes post-processed} in order to * ensure that placeholders have been replaced by actual default values and * in order to enforce {@code @AliasFor} semantics.</li> * </ol> * @param annotatedElement the element that is annotated with the supplied annotation; * may be {@code null} if unknown * @param annotation the annotation to retrieve the attributes for * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested annotations into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @return the annotation attributes (a specialized Map) with attribute names as keys * and corresponding attribute values as values (never {@code null}) * @since 4.2 * @see #postProcessAnnotationAttributes */
static AnnotationAttributes retrieveAnnotationAttributes(@Nullable Object annotatedElement, Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { Class<? extends Annotation> annotationType = annotation.annotationType(); AnnotationAttributes attributes = new AnnotationAttributes(annotationType); for (Method method : getAttributeMethods(annotationType)) { try { Object attributeValue = method.invoke(annotation); Object defaultValue = method.getDefaultValue(); if (defaultValue != null && ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) { attributeValue = new DefaultValueHolder(defaultValue); } attributes.put(method.getName(), adaptValue(annotatedElement, attributeValue, classValuesAsString, nestedAnnotationsAsMap)); } catch (Throwable ex) { if (ex instanceof InvocationTargetException) { Throwable targetException = ((InvocationTargetException) ex).getTargetException(); rethrowAnnotationConfigurationException(targetException); } throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex); } } return attributes; }
Adapt the given value according to the given class and nested annotation settings.

Nested annotations will be synthesized.

Params:
  • annotatedElement – the element that is annotated, used for contextual logging; may be null if unknown
  • value – the annotation attribute value
  • classValuesAsString – whether to convert Class references into Strings (for compatibility with AnnotationMetadata) or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested annotations into AnnotationAttributes maps (for compatibility with AnnotationMetadata) or to preserve them as Annotation instances
Returns:the adapted value, or the original value if no adaptation is needed
/** * Adapt the given value according to the given class and nested annotation settings. * <p>Nested annotations will be * {@linkplain #synthesizeAnnotation(Annotation, AnnotatedElement) synthesized}. * @param annotatedElement the element that is annotated, used for contextual * logging; may be {@code null} if unknown * @param value the annotation attribute value * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested annotations into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @return the adapted value, or the original value if no adaptation is needed */
@Nullable static Object adaptValue(@Nullable Object annotatedElement, @Nullable Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { if (classValuesAsString) { if (value instanceof Class) { return ((Class<?>) value).getName(); } else if (value instanceof Class[]) { Class<?>[] clazzArray = (Class<?>[]) value; String[] classNames = new String[clazzArray.length]; for (int i = 0; i < clazzArray.length; i++) { classNames[i] = clazzArray[i].getName(); } return classNames; } } if (value instanceof Annotation) { Annotation annotation = (Annotation) value; if (nestedAnnotationsAsMap) { return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString, true); } else { return synthesizeAnnotation(annotation, annotatedElement); } } if (value instanceof Annotation[]) { Annotation[] annotations = (Annotation[]) value; if (nestedAnnotationsAsMap) { AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[annotations.length]; for (int i = 0; i < annotations.length; i++) { mappedAnnotations[i] = getAnnotationAttributes(annotatedElement, annotations[i], classValuesAsString, true); } return mappedAnnotations; } else { return synthesizeAnnotationArray(annotations, annotatedElement); } } // Fallback return value; }
Register the annotation-declared default values for the given attributes, if available.
Params:
  • attributes – the annotation attributes to process
Since:4.3.2
/** * Register the annotation-declared default values for the given attributes, * if available. * @param attributes the annotation attributes to process * @since 4.3.2 */
public static void registerDefaultValues(AnnotationAttributes attributes) { // Only do defaults scanning for public annotations; we'd run into // IllegalAccessExceptions otherwise, and we don't want to mess with // accessibility in a SecurityManager environment. Class<? extends Annotation> annotationType = attributes.annotationType(); if (annotationType != null && Modifier.isPublic(annotationType.getModifiers())) { // Check declared default values of attributes in the annotation type. for (Method annotationAttribute : getAttributeMethods(annotationType)) { String attributeName = annotationAttribute.getName(); Object defaultValue = annotationAttribute.getDefaultValue(); if (defaultValue != null && !attributes.containsKey(attributeName)) { if (defaultValue instanceof Annotation) { defaultValue = getAnnotationAttributes((Annotation) defaultValue, false, true); } else if (defaultValue instanceof Annotation[]) { Annotation[] realAnnotations = (Annotation[]) defaultValue; AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; for (int i = 0; i < realAnnotations.length; i++) { mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], false, true); } defaultValue = mappedAnnotations; } attributes.put(attributeName, new DefaultValueHolder(defaultValue)); } } } }
Post-process the supplied AnnotationAttributes, preserving nested annotations as Annotation instances.

Specifically, this method enforces attribute alias semantics for annotation attributes that are annotated with @AliasFor and replaces default value placeholders with their original default values.

Params:
  • annotatedElement – the element that is annotated with an annotation or annotation hierarchy from which the supplied attributes were created; may be null if unknown
  • attributes – the annotation attributes to post-process
  • classValuesAsString – whether to convert Class references into Strings (for compatibility with AnnotationMetadata) or to preserve them as Class references
See Also:
Since:4.3.2
/** * Post-process the supplied {@link AnnotationAttributes}, preserving nested * annotations as {@code Annotation} instances. * <p>Specifically, this method enforces <em>attribute alias</em> semantics * for annotation attributes that are annotated with {@link AliasFor @AliasFor} * and replaces default value placeholders with their original default values. * @param annotatedElement the element that is annotated with an annotation or * annotation hierarchy from which the supplied attributes were created; * may be {@code null} if unknown * @param attributes the annotation attributes to post-process * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @since 4.3.2 * @see #postProcessAnnotationAttributes(Object, AnnotationAttributes, boolean, boolean) * @see #getDefaultValue(Class, String) */
public static void postProcessAnnotationAttributes(@Nullable Object annotatedElement, AnnotationAttributes attributes, boolean classValuesAsString) { postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, false); }
Post-process the supplied AnnotationAttributes.

Specifically, this method enforces attribute alias semantics for annotation attributes that are annotated with @AliasFor and replaces default value placeholders with their original default values.

Params:
  • annotatedElement – the element that is annotated with an annotation or annotation hierarchy from which the supplied attributes were created; may be null if unknown
  • attributes – the annotation attributes to post-process
  • classValuesAsString – whether to convert Class references into Strings (for compatibility with AnnotationMetadata) or to preserve them as Class references
  • nestedAnnotationsAsMap – whether to convert nested annotations into AnnotationAttributes maps (for compatibility with AnnotationMetadata) or to preserve them as Annotation instances
See Also:
Since:4.2
/** * Post-process the supplied {@link AnnotationAttributes}. * <p>Specifically, this method enforces <em>attribute alias</em> semantics * for annotation attributes that are annotated with {@link AliasFor @AliasFor} * and replaces default value placeholders with their original default values. * @param annotatedElement the element that is annotated with an annotation or * annotation hierarchy from which the supplied attributes were created; * may be {@code null} if unknown * @param attributes the annotation attributes to post-process * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested annotations into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @since 4.2 * @see #retrieveAnnotationAttributes(Object, Annotation, boolean, boolean) * @see #getDefaultValue(Class, String) */
static void postProcessAnnotationAttributes(@Nullable Object annotatedElement, @Nullable AnnotationAttributes attributes, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { if (attributes == null) { return; } Class<? extends Annotation> annotationType = attributes.annotationType(); // Track which attribute values have already been replaced so that we can short // circuit the search algorithms. Set<String> valuesAlreadyReplaced = new HashSet<>(); if (!attributes.validated) { // Validate @AliasFor configuration Map<String, List<String>> aliasMap = getAttributeAliasMap(annotationType); aliasMap.forEach((attributeName, aliasedAttributeNames) -> { if (valuesAlreadyReplaced.contains(attributeName)) { return; } Object value = attributes.get(attributeName); boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder)); for (String aliasedAttributeName : aliasedAttributeNames) { if (valuesAlreadyReplaced.contains(aliasedAttributeName)) { continue; } Object aliasedValue = attributes.get(aliasedAttributeName); boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder)); // Something to validate or replace with an alias? if (valuePresent || aliasPresent) { if (valuePresent && aliasPresent) { // Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals(). if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) { String elementAsString = (annotatedElement != null ? annotatedElement.toString() : "unknown element"); throw new AnnotationConfigurationException(String.format( "In AnnotationAttributes for annotation [%s] declared on %s, " + "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " + "but only one is permitted.", attributes.displayName, elementAsString, attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue))); } } else if (aliasPresent) { // Replace value with aliasedValue attributes.put(attributeName, adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap)); valuesAlreadyReplaced.add(attributeName); } else { // Replace aliasedValue with value attributes.put(aliasedAttributeName, adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap)); valuesAlreadyReplaced.add(aliasedAttributeName); } } } }); attributes.validated = true; } // Replace any remaining placeholders with actual default values for (Map.Entry<String, Object> attributeEntry : attributes.entrySet()) { String attributeName = attributeEntry.getKey(); if (valuesAlreadyReplaced.contains(attributeName)) { continue; } Object value = attributeEntry.getValue(); if (value instanceof DefaultValueHolder) { value = ((DefaultValueHolder) value).defaultValue; attributes.put(attributeName, adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap)); } } }
Retrieve the value of the value attribute of a single-element Annotation, given an annotation instance.
Params:
  • annotation – the annotation instance from which to retrieve the value
See Also:
Returns:the attribute value, or null if not found unless the attribute value cannot be retrieved due to an AnnotationConfigurationException, in which case such an exception will be rethrown
/** * Retrieve the <em>value</em> of the {@code value} attribute of a * single-element Annotation, given an annotation instance. * @param annotation the annotation instance from which to retrieve the value * @return the attribute value, or {@code null} if not found unless the attribute * value cannot be retrieved due to an {@link AnnotationConfigurationException}, * in which case such an exception will be rethrown * @see #getValue(Annotation, String) */
@Nullable public static Object getValue(Annotation annotation) { return getValue(annotation, VALUE); }
Retrieve the value of a named attribute, given an annotation instance.
Params:
  • annotation – the annotation instance from which to retrieve the value
  • attributeName – the name of the attribute value to retrieve
See Also:
Returns:the attribute value, or null if not found unless the attribute value cannot be retrieved due to an AnnotationConfigurationException, in which case such an exception will be rethrown
/** * Retrieve the <em>value</em> of a named attribute, given an annotation instance. * @param annotation the annotation instance from which to retrieve the value * @param attributeName the name of the attribute value to retrieve * @return the attribute value, or {@code null} if not found unless the attribute * value cannot be retrieved due to an {@link AnnotationConfigurationException}, * in which case such an exception will be rethrown * @see #getValue(Annotation) * @see #rethrowAnnotationConfigurationException(Throwable) */
@Nullable public static Object getValue(@Nullable Annotation annotation, @Nullable String attributeName) { if (annotation == null || !StringUtils.hasText(attributeName)) { return null; } try { Method method = annotation.annotationType().getDeclaredMethod(attributeName); ReflectionUtils.makeAccessible(method); return method.invoke(annotation); } catch (NoSuchMethodException ex) { return null; } catch (InvocationTargetException ex) { rethrowAnnotationConfigurationException(ex.getTargetException()); throw new IllegalStateException( "Could not obtain value for annotation attribute '" + attributeName + "' in " + annotation, ex); } catch (Throwable ex) { handleIntrospectionFailure(annotation.getClass(), ex); return null; } }
Retrieve the default value of the value attribute of a single-element Annotation, given an annotation instance.
Params:
  • annotation – the annotation instance from which to retrieve the default value
See Also:
Returns:the default value, or null if not found
/** * Retrieve the <em>default value</em> of the {@code value} attribute * of a single-element Annotation, given an annotation instance. * @param annotation the annotation instance from which to retrieve the default value * @return the default value, or {@code null} if not found * @see #getDefaultValue(Annotation, String) */
@Nullable public static Object getDefaultValue(Annotation annotation) { return getDefaultValue(annotation, VALUE); }
Retrieve the default value of a named attribute, given an annotation instance.
Params:
  • annotation – the annotation instance from which to retrieve the default value
  • attributeName – the name of the attribute value to retrieve
See Also:
Returns:the default value of the named attribute, or null if not found
/** * Retrieve the <em>default value</em> of a named attribute, given an annotation instance. * @param annotation the annotation instance from which to retrieve the default value * @param attributeName the name of the attribute value to retrieve * @return the default value of the named attribute, or {@code null} if not found * @see #getDefaultValue(Class, String) */
@Nullable public static Object getDefaultValue(@Nullable Annotation annotation, @Nullable String attributeName) { if (annotation == null) { return null; } return getDefaultValue(annotation.annotationType(), attributeName); }
Retrieve the default value of the value attribute of a single-element Annotation, given the annotation type.
Params:
  • annotationType – the annotation type for which the default value should be retrieved
See Also:
Returns:the default value, or null if not found
/** * Retrieve the <em>default value</em> of the {@code value} attribute * of a single-element Annotation, given the {@link Class annotation type}. * @param annotationType the <em>annotation type</em> for which the default value should be retrieved * @return the default value, or {@code null} if not found * @see #getDefaultValue(Class, String) */
@Nullable public static Object getDefaultValue(Class<? extends Annotation> annotationType) { return getDefaultValue(annotationType, VALUE); }
Retrieve the default value of a named attribute, given the annotation type.
Params:
  • annotationType – the annotation type for which the default value should be retrieved
  • attributeName – the name of the attribute value to retrieve.
See Also:
Returns:the default value of the named attribute, or null if not found
/** * Retrieve the <em>default value</em> of a named attribute, given the * {@link Class annotation type}. * @param annotationType the <em>annotation type</em> for which the default value should be retrieved * @param attributeName the name of the attribute value to retrieve. * @return the default value of the named attribute, or {@code null} if not found * @see #getDefaultValue(Annotation, String) */
@Nullable public static Object getDefaultValue( @Nullable Class<? extends Annotation> annotationType, @Nullable String attributeName) { if (annotationType == null || !StringUtils.hasText(attributeName)) { return null; } try { return annotationType.getDeclaredMethod(attributeName).getDefaultValue(); } catch (Throwable ex) { handleIntrospectionFailure(annotationType, ex); return null; } }
Synthesize an annotation from the supplied annotation by wrapping it in a dynamic proxy that transparently enforces attribute alias semantics for annotation attributes that are annotated with @AliasFor.
Params:
  • annotation – the annotation to synthesize
Throws:
See Also:
Returns:the synthesized annotation, if the supplied annotation is synthesizable; null if the supplied annotation is null; otherwise, the supplied annotation unmodified
Since:4.2
/** * <em>Synthesize</em> an annotation from the supplied {@code annotation} * by wrapping it in a dynamic proxy that transparently enforces * <em>attribute alias</em> semantics for annotation attributes that are * annotated with {@link AliasFor @AliasFor}. * @param annotation the annotation to synthesize * @return the synthesized annotation, if the supplied annotation is * <em>synthesizable</em>; {@code null} if the supplied annotation is * {@code null}; otherwise, the supplied annotation unmodified * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) */
static <A extends Annotation> A synthesizeAnnotation(A annotation) { return synthesizeAnnotation(annotation, null); }
Synthesize an annotation from the supplied annotation by wrapping it in a dynamic proxy that transparently enforces attribute alias semantics for annotation attributes that are annotated with @AliasFor.
Params:
  • annotation – the annotation to synthesize
  • annotatedElement – the element that is annotated with the supplied annotation; may be null if unknown
Throws:
See Also:
Returns:the synthesized annotation if the supplied annotation is synthesizable; null if the supplied annotation is null; otherwise the supplied annotation unmodified
Since:4.2
/** * <em>Synthesize</em> an annotation from the supplied {@code annotation} * by wrapping it in a dynamic proxy that transparently enforces * <em>attribute alias</em> semantics for annotation attributes that are * annotated with {@link AliasFor @AliasFor}. * @param annotation the annotation to synthesize * @param annotatedElement the element that is annotated with the supplied * annotation; may be {@code null} if unknown * @return the synthesized annotation if the supplied annotation is * <em>synthesizable</em>; {@code null} if the supplied annotation is * {@code null}; otherwise the supplied annotation unmodified * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) * @see #synthesizeAnnotation(Class) */
public static <A extends Annotation> A synthesizeAnnotation( A annotation, @Nullable AnnotatedElement annotatedElement) { return synthesizeAnnotation(annotation, (Object) annotatedElement); } @SuppressWarnings("unchecked") static <A extends Annotation> A synthesizeAnnotation(A annotation, @Nullable Object annotatedElement) { if (annotation instanceof SynthesizedAnnotation || hasPlainJavaAnnotationsOnly(annotatedElement)) { return annotation; } Class<? extends Annotation> annotationType = annotation.annotationType(); if (!isSynthesizable(annotationType)) { return annotation; } DefaultAnnotationAttributeExtractor attributeExtractor = new DefaultAnnotationAttributeExtractor(annotation, annotatedElement); InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); // Can always expose Spring's SynthesizedAnnotation marker since we explicitly check for a // synthesizable annotation before (which needs to declare @AliasFor from the same package) Class<?>[] exposedInterfaces = new Class<?>[] {annotationType, SynthesizedAnnotation.class}; return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler); }
Synthesize an annotation from the supplied map of annotation attributes by wrapping the map in a dynamic proxy that implements an annotation of the specified annotationType and transparently enforces attribute alias semantics for annotation attributes that are annotated with @AliasFor.

The supplied map must contain a key-value pair for every attribute defined in the supplied annotationType that is not aliased or does not have a default value. Nested maps and nested arrays of maps will be recursively synthesized into nested annotations or nested arrays of annotations, respectively.

Note that AnnotationAttributes is a specialized type of Map that is an ideal candidate for this method's attributes argument.

Params:
  • attributes – the map of annotation attributes to synthesize
  • annotationType – the type of annotation to synthesize
  • annotatedElement – the element that is annotated with the annotation corresponding to the supplied attributes; may be null if unknown
Throws:
See Also:
Returns:the synthesized annotation
Since:4.2
/** * <em>Synthesize</em> an annotation from the supplied map of annotation * attributes by wrapping the map in a dynamic proxy that implements an * annotation of the specified {@code annotationType} and transparently * enforces <em>attribute alias</em> semantics for annotation attributes * that are annotated with {@link AliasFor @AliasFor}. * <p>The supplied map must contain a key-value pair for every attribute * defined in the supplied {@code annotationType} that is not aliased or * does not have a default value. Nested maps and nested arrays of maps * will be recursively synthesized into nested annotations or nested * arrays of annotations, respectively. * <p>Note that {@link AnnotationAttributes} is a specialized type of * {@link Map} that is an ideal candidate for this method's * {@code attributes} argument. * @param attributes the map of annotation attributes to synthesize * @param annotationType the type of annotation to synthesize * @param annotatedElement the element that is annotated with the annotation * corresponding to the supplied attributes; may be {@code null} if unknown * @return the synthesized annotation * @throws IllegalArgumentException if a required attribute is missing or if an * attribute is not of the correct type * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) * @see #synthesizeAnnotation(Class) * @see #getAnnotationAttributes(AnnotatedElement, Annotation) * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) */
@SuppressWarnings("unchecked") public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object> attributes, Class<A> annotationType, @Nullable AnnotatedElement annotatedElement) { MapAnnotationAttributeExtractor attributeExtractor = new MapAnnotationAttributeExtractor(attributes, annotationType, annotatedElement); InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); Class<?>[] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ? new Class<?>[] {annotationType, SynthesizedAnnotation.class} : new Class<?>[] {annotationType}); return (A) Proxy.newProxyInstance(annotationType.getClassLoader(), exposedInterfaces, handler); }
Synthesize an annotation from its default attributes values.

This method simply delegates to synthesizeAnnotation(Map<String,Object>, Class<Annotation>, AnnotatedElement), supplying an empty map for the source attribute values and null for the AnnotatedElement.

Params:
  • annotationType – the type of annotation to synthesize
Throws:
See Also:
Returns:the synthesized annotation
Since:4.2
/** * <em>Synthesize</em> an annotation from its default attributes values. * <p>This method simply delegates to * {@link #synthesizeAnnotation(Map, Class, AnnotatedElement)}, * supplying an empty map for the source attribute values and {@code null} * for the {@link AnnotatedElement}. * @param annotationType the type of annotation to synthesize * @return the synthesized annotation * @throws IllegalArgumentException if a required attribute is missing * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) * @see #synthesizeAnnotation(Annotation, AnnotatedElement) */
public static <A extends Annotation> A synthesizeAnnotation(Class<A> annotationType) { return synthesizeAnnotation(Collections.emptyMap(), annotationType, null); }
Synthesize an array of annotations from the supplied array of annotations by creating a new array of the same size and type and populating it with synthesized versions of the annotations from the input array.
Params:
  • annotations – the array of annotations to synthesize
  • annotatedElement – the element that is annotated with the supplied array of annotations; may be null if unknown
Throws:
See Also:
Returns:a new array of synthesized annotations, or null if the supplied array is null
Since:4.2
/** * <em>Synthesize</em> an array of annotations from the supplied array * of {@code annotations} by creating a new array of the same size and * type and populating it with {@linkplain #synthesizeAnnotation(Annotation) * synthesized} versions of the annotations from the input array. * @param annotations the array of annotations to synthesize * @param annotatedElement the element that is annotated with the supplied * array of annotations; may be {@code null} if unknown * @return a new array of synthesized annotations, or {@code null} if * the supplied array is {@code null} * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) */
static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, @Nullable Object annotatedElement) { if (hasPlainJavaAnnotationsOnly(annotatedElement)) { return annotations; } Annotation[] synthesized = (Annotation[]) Array.newInstance( annotations.getClass().getComponentType(), annotations.length); for (int i = 0; i < annotations.length; i++) { synthesized[i] = synthesizeAnnotation(annotations[i], annotatedElement); } return synthesized; }
Synthesize an array of annotations from the supplied array of maps of annotation attributes by creating a new array of annotationType with the same size and populating it with synthesized versions of the maps from the input array.
Params:
  • maps – the array of maps of annotation attributes to synthesize
  • annotationType – the type of annotations to synthesize (never null)
Throws:
See Also:
Returns:a new array of synthesized annotations, or null if the supplied array is null
Since:4.2.1
/** * <em>Synthesize</em> an array of annotations from the supplied array * of {@code maps} of annotation attributes by creating a new array of * {@code annotationType} with the same size and populating it with * {@linkplain #synthesizeAnnotation(Map, Class, AnnotatedElement) * synthesized} versions of the maps from the input array. * @param maps the array of maps of annotation attributes to synthesize * @param annotationType the type of annotations to synthesize * (never {@code null}) * @return a new array of synthesized annotations, or {@code null} if * the supplied array is {@code null} * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2.1 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) * @see #synthesizeAnnotationArray(Annotation[], Object) */
@SuppressWarnings("unchecked") @Nullable static <A extends Annotation> A[] synthesizeAnnotationArray( @Nullable Map<String, Object>[] maps, Class<A> annotationType) { if (maps == null) { return null; } A[] synthesized = (A[]) Array.newInstance(annotationType, maps.length); for (int i = 0; i < maps.length; i++) { synthesized[i] = synthesizeAnnotation(maps[i], annotationType, null); } return synthesized; }
Get a map of all attribute aliases declared via @AliasFor in the supplied annotation type.

The map is keyed by attribute name with each value representing a list of names of aliased attributes.

For explicit alias pairs such as x and y (i.e. where x is an @AliasFor("y") and y is an @AliasFor("x"), there will be two entries in the map: x -> (y) and y -> (x).

For implicit aliases (i.e. attributes that are declared as attribute overrides for the same attribute in the same meta-annotation), there will be n entries in the map. For example, if x, y, and z are implicit aliases, the map will contain the following entries: x -> (y, z), y -> (x, z), z -> (x, y).

An empty return value implies that the annotation does not declare any attribute aliases.

Params:
  • annotationType – the annotation type to find attribute aliases in
Returns:a map containing attribute aliases (never null)
Since:4.2
/** * Get a map of all attribute aliases declared via {@code @AliasFor} * in the supplied annotation type. * <p>The map is keyed by attribute name with each value representing * a list of names of aliased attributes. * <p>For <em>explicit</em> alias pairs such as x and y (i.e. where x * is an {@code @AliasFor("y")} and y is an {@code @AliasFor("x")}, there * will be two entries in the map: {@code x -> (y)} and {@code y -> (x)}. * <p>For <em>implicit</em> aliases (i.e. attributes that are declared * as attribute overrides for the same attribute in the same meta-annotation), * there will be n entries in the map. For example, if x, y, and z are * implicit aliases, the map will contain the following entries: * {@code x -> (y, z)}, {@code y -> (x, z)}, {@code z -> (x, y)}. * <p>An empty return value implies that the annotation does not declare * any attribute aliases. * @param annotationType the annotation type to find attribute aliases in * @return a map containing attribute aliases (never {@code null}) * @since 4.2 */
static Map<String, List<String>> getAttributeAliasMap(@Nullable Class<? extends Annotation> annotationType) { if (annotationType == null) { return Collections.emptyMap(); } Map<String, List<String>> map = attributeAliasesCache.get(annotationType); if (map != null) { return map; } map = new LinkedHashMap<>(); for (Method attribute : getAttributeMethods(annotationType)) { List<String> aliasNames = getAttributeAliasNames(attribute); if (!aliasNames.isEmpty()) { map.put(attribute.getName(), aliasNames); } } attributeAliasesCache.put(annotationType, map); return map; }
Check whether we can expose our SynthesizedAnnotation marker for the given annotation type.
Params:
  • annotationType – the annotation type that we are about to create a synthesized proxy for
/** * Check whether we can expose our {@link SynthesizedAnnotation} marker for the given annotation type. * @param annotationType the annotation type that we are about to create a synthesized proxy for */
private static boolean canExposeSynthesizedMarker(Class<? extends Annotation> annotationType) { try { return (Class.forName(SynthesizedAnnotation.class.getName(), false, annotationType.getClassLoader()) == SynthesizedAnnotation.class); } catch (ClassNotFoundException ex) { return false; } }
Determine if annotations of the supplied annotationType are synthesizable (i.e. in need of being wrapped in a dynamic proxy that provides functionality above that of a standard JDK annotation).

Specifically, an annotation is synthesizable if it declares any attributes that are configured as aliased pairs via @AliasFor or if any nested annotations used by the annotation declare such aliased pairs.

See Also:
Since:4.2
/** * Determine if annotations of the supplied {@code annotationType} are * <em>synthesizable</em> (i.e. in need of being wrapped in a dynamic * proxy that provides functionality above that of a standard JDK * annotation). * <p>Specifically, an annotation is <em>synthesizable</em> if it declares * any attributes that are configured as <em>aliased pairs</em> via * {@link AliasFor @AliasFor} or if any nested annotations used by the * annotation declare such <em>aliased pairs</em>. * @since 4.2 * @see SynthesizedAnnotation * @see SynthesizedAnnotationInvocationHandler */
@SuppressWarnings("unchecked") private static boolean isSynthesizable(Class<? extends Annotation> annotationType) { if (hasPlainJavaAnnotationsOnly(annotationType)) { return false; } Boolean synthesizable = synthesizableCache.get(annotationType); if (synthesizable != null) { return synthesizable; } synthesizable = Boolean.FALSE; for (Method attribute : getAttributeMethods(annotationType)) { if (!getAttributeAliasNames(attribute).isEmpty()) { synthesizable = Boolean.TRUE; break; } Class<?> returnType = attribute.getReturnType(); if (Annotation[].class.isAssignableFrom(returnType)) { Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType.getComponentType(); if (isSynthesizable(nestedAnnotationType)) { synthesizable = Boolean.TRUE; break; } } else if (Annotation.class.isAssignableFrom(returnType)) { Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType; if (isSynthesizable(nestedAnnotationType)) { synthesizable = Boolean.TRUE; break; } } } synthesizableCache.put(annotationType, synthesizable); return synthesizable; }
Get the names of the aliased attributes configured via @AliasFor for the supplied annotation attribute.
Params:
  • attribute – the attribute to find aliases for
Throws:
See Also:
Returns:the names of the aliased attributes (never null, though potentially empty)
Since:4.2
/** * Get the names of the aliased attributes configured via * {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}. * @param attribute the attribute to find aliases for * @return the names of the aliased attributes (never {@code null}, though * potentially <em>empty</em>) * @throws IllegalArgumentException if the supplied attribute method is * {@code null} or not from an annotation * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #getAttributeOverrideName(Method, Class) */
static List<String> getAttributeAliasNames(Method attribute) { AliasDescriptor descriptor = AliasDescriptor.from(attribute); return (descriptor != null ? descriptor.getAttributeAliasNames() : Collections.emptyList()); }
Get the name of the overridden attribute configured via @AliasFor for the supplied annotation attribute.
Params:
  • attribute – the attribute from which to retrieve the override (never null)
  • metaAnnotationType – the type of meta-annotation in which the overridden attribute is allowed to be declared
Throws:
Returns:the name of the overridden attribute, or null if not found or not applicable for the specified meta-annotation type
Since:4.2
/** * Get the name of the overridden attribute configured via * {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}. * @param attribute the attribute from which to retrieve the override * (never {@code null}) * @param metaAnnotationType the type of meta-annotation in which the * overridden attribute is allowed to be declared * @return the name of the overridden attribute, or {@code null} if not * found or not applicable for the specified meta-annotation type * @throws IllegalArgumentException if the supplied attribute method is * {@code null} or not from an annotation, or if the supplied meta-annotation * type is {@code null} or {@link Annotation} * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 */
@Nullable static String getAttributeOverrideName(Method attribute, @Nullable Class<? extends Annotation> metaAnnotationType) { AliasDescriptor descriptor = AliasDescriptor.from(attribute); return (descriptor != null && metaAnnotationType != null ? descriptor.getAttributeOverrideName(metaAnnotationType) : null); }
Get all methods declared in the supplied annotationType that match Java's requirements for annotation attributes.

All methods in the returned list will be made accessible.

Params:
  • annotationType – the type in which to search for attribute methods (never null)
Returns:all annotation attribute methods in the specified annotation type (never null, though potentially empty)
Since:4.2
/** * Get all methods declared in the supplied {@code annotationType} that * match Java's requirements for annotation <em>attributes</em>. * <p>All methods in the returned list will be * {@linkplain ReflectionUtils#makeAccessible(Method) made accessible}. * @param annotationType the type in which to search for attribute methods * (never {@code null}) * @return all annotation attribute methods in the specified annotation * type (never {@code null}, though potentially <em>empty</em>) * @since 4.2 */
static List<Method> getAttributeMethods(Class<? extends Annotation> annotationType) { List<Method> methods = attributeMethodsCache.get(annotationType); if (methods != null) { return methods; } methods = new ArrayList<>(); for (Method method : annotationType.getDeclaredMethods()) { if (isAttributeMethod(method)) { ReflectionUtils.makeAccessible(method); methods.add(method); } } attributeMethodsCache.put(annotationType, methods); return methods; }
Get the annotation with the supplied annotationName on the supplied element.
Params:
  • element – the element to search on
  • annotationName – the fully qualified class name of the annotation type to find
Returns:the annotation if found; null otherwise
Since:4.2
/** * Get the annotation with the supplied {@code annotationName} on the * supplied {@code element}. * @param element the element to search on * @param annotationName the fully qualified class name of the annotation * type to find * @return the annotation if found; {@code null} otherwise * @since 4.2 */
@Nullable static Annotation getAnnotation(AnnotatedElement element, String annotationName) { for (Annotation annotation : element.getAnnotations()) { if (annotation.annotationType().getName().equals(annotationName)) { return annotation; } } return null; }
Determine if the supplied method is an annotation attribute method.
Params:
  • method – the method to check
Returns:true if the method is an attribute method
Since:4.2
/** * Determine if the supplied {@code method} is an annotation attribute method. * @param method the method to check * @return {@code true} if the method is an attribute method * @since 4.2 */
static boolean isAttributeMethod(@Nullable Method method) { return (method != null && method.getParameterCount() == 0 && method.getReturnType() != void.class); }
Determine if the supplied method is an "annotationType" method.
See Also:
Returns:true if the method is an "annotationType" method
Since:4.2
/** * Determine if the supplied method is an "annotationType" method. * @return {@code true} if the method is an "annotationType" method * @since 4.2 * @see Annotation#annotationType() */
static boolean isAnnotationTypeMethod(@Nullable Method method) { return (method != null && method.getName().equals("annotationType") && method.getParameterCount() == 0); }
Resolve the container type for the supplied repeatable annotationType.

Automatically detects a container annotation declared via Repeatable. If the supplied annotation type is not annotated with @Repeatable, this method simply returns null.

Since:4.2
/** * Resolve the container type for the supplied repeatable {@code annotationType}. * <p>Automatically detects a <em>container annotation</em> declared via * {@link java.lang.annotation.Repeatable}. If the supplied annotation type * is not annotated with {@code @Repeatable}, this method simply returns * {@code null}. * @since 4.2 */
@Nullable static Class<? extends Annotation> resolveContainerAnnotationType(Class<? extends Annotation> annotationType) { Repeatable repeatable = getAnnotation(annotationType, Repeatable.class); return (repeatable != null ? repeatable.value() : null); }
If the supplied throwable is an AnnotationConfigurationException, it will be cast to an AnnotationConfigurationException and thrown, allowing it to propagate to the caller.

Otherwise, this method does nothing.

Params:
  • ex – the throwable to inspect
Since:4.2
/** * If the supplied throwable is an {@link AnnotationConfigurationException}, * it will be cast to an {@code AnnotationConfigurationException} and thrown, * allowing it to propagate to the caller. * <p>Otherwise, this method does nothing. * @param ex the throwable to inspect * @since 4.2 */
static void rethrowAnnotationConfigurationException(Throwable ex) { if (ex instanceof AnnotationConfigurationException) { throw (AnnotationConfigurationException) ex; } }
Handle the supplied annotation introspection exception.

If the supplied exception is an AnnotationConfigurationException, it will simply be thrown, allowing it to propagate to the caller, and nothing will be logged.

Otherwise, this method logs an introspection failure (in particular TypeNotPresentExceptions) before moving on, assuming nested Class values were not resolvable within annotation attributes and thereby effectively pretending there were no annotations on the specified element.

Params:
  • element – the element that we tried to introspect annotations on
  • ex – the exception that we encountered
See Also:
/** * Handle the supplied annotation introspection exception. * <p>If the supplied exception is an {@link AnnotationConfigurationException}, * it will simply be thrown, allowing it to propagate to the caller, and * nothing will be logged. * <p>Otherwise, this method logs an introspection failure (in particular * {@code TypeNotPresentExceptions}) before moving on, assuming nested * Class values were not resolvable within annotation attributes and * thereby effectively pretending there were no annotations on the specified * element. * @param element the element that we tried to introspect annotations on * @param ex the exception that we encountered * @see #rethrowAnnotationConfigurationException */
static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) { rethrowAnnotationConfigurationException(ex); Log loggerToUse = logger; if (loggerToUse == null) { loggerToUse = LogFactory.getLog(AnnotationUtils.class); logger = loggerToUse; } if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) { // Meta-annotation or (default) value lookup on an annotation type if (loggerToUse.isDebugEnabled()) { loggerToUse.debug("Failed to meta-introspect annotation " + element + ": " + ex); } } else { // Direct annotation lookup on regular Class, Method, Field if (loggerToUse.isInfoEnabled()) { loggerToUse.info("Failed to introspect annotations on " + element + ": " + ex); } } }
Clear the internal annotation metadata cache.
Since:4.3.15
/** * Clear the internal annotation metadata cache. * @since 4.3.15 */
public static void clearCache() { findAnnotationCache.clear(); metaPresentCache.clear(); declaredAnnotationsCache.clear(); annotatedBaseTypeCache.clear(); synthesizableCache.clear(); attributeAliasesCache.clear(); attributeMethodsCache.clear(); aliasDescriptorCache.clear(); }
Cache key for the AnnotatedElement cache.
/** * Cache key for the AnnotatedElement cache. */
private static final class AnnotationCacheKey implements Comparable<AnnotationCacheKey> { private final AnnotatedElement element; private final Class<? extends Annotation> annotationType; public AnnotationCacheKey(AnnotatedElement element, Class<? extends Annotation> annotationType) { this.element = element; this.annotationType = annotationType; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof AnnotationCacheKey)) { return false; } AnnotationCacheKey otherKey = (AnnotationCacheKey) other; return (this.element.equals(otherKey.element) && this.annotationType.equals(otherKey.annotationType)); } @Override public int hashCode() { return (this.element.hashCode() * 29 + this.annotationType.hashCode()); } @Override public String toString() { return "@" + this.annotationType + " on " + this.element; } @Override public int compareTo(AnnotationCacheKey other) { int result = this.element.toString().compareTo(other.element.toString()); if (result == 0) { result = this.annotationType.getName().compareTo(other.annotationType.getName()); } return result; } } private static class AnnotationCollector<A extends Annotation> { private final Class<A> annotationType; @Nullable private final Class<? extends Annotation> containerAnnotationType; private final Set<AnnotatedElement> visited = new HashSet<>(); private final Set<A> result = new LinkedHashSet<>(); AnnotationCollector(Class<A> annotationType,@Nullable Class<? extends Annotation> containerAnnotationType) { this.annotationType = annotationType; this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType : resolveContainerAnnotationType(annotationType)); } Set<A> getResult(AnnotatedElement element) { process(element); return Collections.unmodifiableSet(this.result); } @SuppressWarnings("unchecked") private void process(AnnotatedElement element) { if (this.visited.add(element)) { try { Annotation[] annotations = getDeclaredAnnotations(element); for (Annotation ann : annotations) { Class<? extends Annotation> currentAnnotationType = ann.annotationType(); if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) { this.result.add(synthesizeAnnotation((A) ann, element)); } else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) { this.result.addAll(getValue(element, ann)); } else if (!isInJavaLangAnnotationPackage(currentAnnotationType)) { process(currentAnnotationType); } } } catch (Throwable ex) { handleIntrospectionFailure(element, ex); } } } @SuppressWarnings("unchecked") private List<A> getValue(AnnotatedElement element, Annotation annotation) { try { List<A> synthesizedAnnotations = new ArrayList<>(); A[] value = (A[]) AnnotationUtils.getValue(annotation); if (value != null) { for (A anno : value) { synthesizedAnnotations.add(synthesizeAnnotation(anno, element)); } } return synthesizedAnnotations; } catch (Throwable ex) { handleIntrospectionFailure(element, ex); } // Unable to read value from repeating annotation container -> ignore it. return Collections.emptyList(); } }
AliasDescriptor encapsulates the declaration of @AliasFor on a given annotation attribute and includes support for validating the configuration of aliases (both explicit and implicit).
See Also:
Since:4.2.1
/** * {@code AliasDescriptor} encapsulates the declaration of {@code @AliasFor} * on a given annotation attribute and includes support for validating * the configuration of aliases (both explicit and implicit). * @since 4.2.1 * @see #from * @see #getAttributeAliasNames * @see #getAttributeOverrideName */
private static final class AliasDescriptor { private final Method sourceAttribute; private final Class<? extends Annotation> sourceAnnotationType; private final String sourceAttributeName; private final Method aliasedAttribute; private final Class<? extends Annotation> aliasedAnnotationType; private final String aliasedAttributeName; private final boolean isAliasPair;
Create an AliasDescriptor from the declaration of @AliasFor on the supplied annotation attribute and validate the configuration of @AliasFor.
Params:
  • attribute – the annotation attribute that is annotated with @AliasFor
See Also:
Returns:an alias descriptor, or null if the attribute is not annotated with @AliasFor
/** * Create an {@code AliasDescriptor} <em>from</em> the declaration * of {@code @AliasFor} on the supplied annotation attribute and * validate the configuration of {@code @AliasFor}. * @param attribute the annotation attribute that is annotated with * {@code @AliasFor} * @return an alias descriptor, or {@code null} if the attribute * is not annotated with {@code @AliasFor} * @see #validateAgainst */
@Nullable public static AliasDescriptor from(Method attribute) { AliasDescriptor descriptor = aliasDescriptorCache.get(attribute); if (descriptor != null) { return descriptor; } AliasFor aliasFor = attribute.getAnnotation(AliasFor.class); if (aliasFor == null) { return null; } descriptor = new AliasDescriptor(attribute, aliasFor); descriptor.validate(); aliasDescriptorCache.put(attribute, descriptor); return descriptor; } @SuppressWarnings("unchecked") private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) { Class<?> declaringClass = sourceAttribute.getDeclaringClass(); this.sourceAttribute = sourceAttribute; this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass; this.sourceAttributeName = sourceAttribute.getName(); this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation() ? this.sourceAnnotationType : aliasFor.annotation()); this.aliasedAttributeName = getAliasedAttributeName(aliasFor, sourceAttribute); if (this.aliasedAnnotationType == this.sourceAnnotationType && this.aliasedAttributeName.equals(this.sourceAttributeName)) { String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] points to " + "itself. Specify 'annotation' to point to a same-named attribute on a meta-annotation.", sourceAttribute.getName(), declaringClass.getName()); throw new AnnotationConfigurationException(msg); } try { this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName); } catch (NoSuchMethodException ex) { String msg = String.format( "Attribute '%s' in annotation [%s] is declared as an @AliasFor nonexistent attribute '%s' in annotation [%s].", this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, this.aliasedAnnotationType.getName()); throw new AnnotationConfigurationException(msg, ex); } this.isAliasPair = (this.sourceAnnotationType == this.aliasedAnnotationType); } private void validate() { // Target annotation is not meta-present? if (!this.isAliasPair && !isAnnotationMetaPresent(this.sourceAnnotationType, this.aliasedAnnotationType)) { String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] declares " + "an alias for attribute '%s' in meta-annotation [%s] which is not meta-present.", this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, this.aliasedAnnotationType.getName()); throw new AnnotationConfigurationException(msg); } if (this.isAliasPair) { AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class); if (mirrorAliasFor == null) { String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s].", this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName); throw new AnnotationConfigurationException(msg); } String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute); if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) { String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s], not [%s].", this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName, mirrorAliasedAttributeName); throw new AnnotationConfigurationException(msg); } } Class<?> returnType = this.sourceAttribute.getReturnType(); Class<?> aliasedReturnType = this.aliasedAttribute.getReturnType(); if (returnType != aliasedReturnType && (!aliasedReturnType.isArray() || returnType != aliasedReturnType.getComponentType())) { String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " + "and attribute '%s' in annotation [%s] must declare the same return type.", this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, this.aliasedAnnotationType.getName()); throw new AnnotationConfigurationException(msg); } if (this.isAliasPair) { validateDefaultValueConfiguration(this.aliasedAttribute); } } private void validateDefaultValueConfiguration(Method aliasedAttribute) { Object defaultValue = this.sourceAttribute.getDefaultValue(); Object aliasedDefaultValue = aliasedAttribute.getDefaultValue(); if (defaultValue == null || aliasedDefaultValue == null) { String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " + "and attribute '%s' in annotation [%s] must declare default values.", this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(), aliasedAttribute.getDeclaringClass().getName()); throw new AnnotationConfigurationException(msg); } if (!ObjectUtils.nullSafeEquals(defaultValue, aliasedDefaultValue)) { String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " + "and attribute '%s' in annotation [%s] must declare the same default value.", this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(), aliasedAttribute.getDeclaringClass().getName()); throw new AnnotationConfigurationException(msg); } }
Validate this descriptor against the supplied descriptor.

This method only validates the configuration of default values for the two descriptors, since other aspects of the descriptors are validated when they are created.

/** * Validate this descriptor against the supplied descriptor. * <p>This method only validates the configuration of default values * for the two descriptors, since other aspects of the descriptors * are validated when they are created. */
private void validateAgainst(AliasDescriptor otherDescriptor) { validateDefaultValueConfiguration(otherDescriptor.sourceAttribute); }
Determine if this descriptor represents an explicit override for an attribute in the supplied metaAnnotationType.
See Also:
/** * Determine if this descriptor represents an explicit override for * an attribute in the supplied {@code metaAnnotationType}. * @see #isAliasFor */
private boolean isOverrideFor(Class<? extends Annotation> metaAnnotationType) { return (this.aliasedAnnotationType == metaAnnotationType); }
Determine if this descriptor and the supplied descriptor both effectively represent aliases for the same attribute in the same target annotation, either explicitly or implicitly.

This method searches the attribute override hierarchy, beginning with this descriptor, in order to detect implicit and transitively implicit aliases.

See Also:
Returns:true if this descriptor and the supplied descriptor effectively alias the same annotation attribute
/** * Determine if this descriptor and the supplied descriptor both * effectively represent aliases for the same attribute in the same * target annotation, either explicitly or implicitly. * <p>This method searches the attribute override hierarchy, beginning * with this descriptor, in order to detect implicit and transitively * implicit aliases. * @return {@code true} if this descriptor and the supplied descriptor * effectively alias the same annotation attribute * @see #isOverrideFor */
private boolean isAliasFor(AliasDescriptor otherDescriptor) { for (AliasDescriptor lhs = this; lhs != null; lhs = lhs.getAttributeOverrideDescriptor()) { for (AliasDescriptor rhs = otherDescriptor; rhs != null; rhs = rhs.getAttributeOverrideDescriptor()) { if (lhs.aliasedAttribute.equals(rhs.aliasedAttribute)) { return true; } } } return false; } public List<String> getAttributeAliasNames() { // Explicit alias pair? if (this.isAliasPair) { return Collections.singletonList(this.aliasedAttributeName); } // Else: search for implicit aliases List<String> aliases = new ArrayList<>(); for (AliasDescriptor otherDescriptor : getOtherDescriptors()) { if (this.isAliasFor(otherDescriptor)) { this.validateAgainst(otherDescriptor); aliases.add(otherDescriptor.sourceAttributeName); } } return aliases; } private List<AliasDescriptor> getOtherDescriptors() { List<AliasDescriptor> otherDescriptors = new ArrayList<>(); for (Method currentAttribute : getAttributeMethods(this.sourceAnnotationType)) { if (!this.sourceAttribute.equals(currentAttribute)) { AliasDescriptor otherDescriptor = AliasDescriptor.from(currentAttribute); if (otherDescriptor != null) { otherDescriptors.add(otherDescriptor); } } } return otherDescriptors; } @Nullable public String getAttributeOverrideName(Class<? extends Annotation> metaAnnotationType) { // Search the attribute override hierarchy, starting with the current attribute for (AliasDescriptor desc = this; desc != null; desc = desc.getAttributeOverrideDescriptor()) { if (desc.isOverrideFor(metaAnnotationType)) { return desc.aliasedAttributeName; } } // Else: explicit attribute override for a different meta-annotation return null; } @Nullable private AliasDescriptor getAttributeOverrideDescriptor() { if (this.isAliasPair) { return null; } return AliasDescriptor.from(this.aliasedAttribute); }
Get the name of the aliased attribute configured via the supplied @AliasFor annotation on the supplied attribute, or the original attribute if no aliased one specified (indicating that the reference goes to a same-named attribute on a meta-annotation).

This method returns the value of either the attribute or value attribute of @AliasFor, ensuring that only one of the attributes has been declared while simultaneously ensuring that at least one of the attributes has been declared.

Params:
  • aliasFor – the @AliasFor annotation from which to retrieve the aliased attribute name
  • attribute – the attribute that is annotated with @AliasFor
Throws:
Returns:the name of the aliased attribute (never null or empty)
/** * Get the name of the aliased attribute configured via the supplied * {@link AliasFor @AliasFor} annotation on the supplied {@code attribute}, * or the original attribute if no aliased one specified (indicating that * the reference goes to a same-named attribute on a meta-annotation). * <p>This method returns the value of either the {@code attribute} * or {@code value} attribute of {@code @AliasFor}, ensuring that only * one of the attributes has been declared while simultaneously ensuring * that at least one of the attributes has been declared. * @param aliasFor the {@code @AliasFor} annotation from which to retrieve * the aliased attribute name * @param attribute the attribute that is annotated with {@code @AliasFor} * @return the name of the aliased attribute (never {@code null} or empty) * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected */
private String getAliasedAttributeName(AliasFor aliasFor, Method attribute) { String attributeName = aliasFor.attribute(); String value = aliasFor.value(); boolean attributeDeclared = StringUtils.hasText(attributeName); boolean valueDeclared = StringUtils.hasText(value); // Ensure user did not declare both 'value' and 'attribute' in @AliasFor if (attributeDeclared && valueDeclared) { String msg = String.format("In @AliasFor declared on attribute '%s' in annotation [%s], attribute 'attribute' " + "and its alias 'value' are present with values of [%s] and [%s], but only one is permitted.", attribute.getName(), attribute.getDeclaringClass().getName(), attributeName, value); throw new AnnotationConfigurationException(msg); } // Either explicit attribute name or pointing to same-named attribute by default attributeName = (attributeDeclared ? attributeName : value); return (StringUtils.hasText(attributeName) ? attributeName.trim() : attribute.getName()); } @Override public String toString() { return String.format("%s: @%s(%s) is an alias for @%s(%s)", getClass().getSimpleName(), this.sourceAnnotationType.getSimpleName(), this.sourceAttributeName, this.aliasedAnnotationType.getSimpleName(), this.aliasedAttributeName); } } private static class DefaultValueHolder { final Object defaultValue; public DefaultValueHolder(Object defaultValue) { this.defaultValue = defaultValue; } } }