/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
General utility methods for finding annotations, meta-annotations, and repeatable annotations on AnnotatedElements
. AnnotatedElementUtils
defines the public API for Spring's meta-annotation programming model with support for annotation attribute
overrides. If you do not need support for annotation attribute overrides, consider using AnnotationUtils
instead.
Note that the features of this class are not provided by the JDK's
introspection facilities themselves.
Annotation Attribute Overrides
Support for meta-annotations with attribute overrides in
composed annotations is provided by all variants of the getMergedAnnotationAttributes()
, getMergedAnnotation()
, getAllMergedAnnotations()
, getMergedRepeatableAnnotations()
, findMergedAnnotationAttributes()
, findMergedAnnotation()
, findAllMergedAnnotations()
, and findMergedRepeatableAnnotations()
methods.
Find vs. Get Semantics
The search algorithms used by methods in this class follow either
find or get semantics. Consult the javadocs for each
individual method for details on which search algorithm is used.
Get semantics are limited to searching for annotations
that are either present on an AnnotatedElement
(i.e. declared locally or inherited) or declared within the annotation hierarchy above the AnnotatedElement
.
Find semantics are much more exhaustive, providing
get semantics plus support for the following:
- Searching on interfaces, if the annotated element is a class
- Searching on superclasses, if the annotated element is a class
- Resolving bridged methods, if the annotated element is a method
- Searching on methods in interfaces, if the annotated element is a method
- Searching on methods in superclasses, if the annotated element is a method
Support for @Inherited
Methods following get semantics will honor the contract of Java's @Inherited
annotation except that locally declared annotations (including custom composed annotations) will be favored over inherited annotations. In contrast, methods following find semantics will completely ignore the presence of @Inherited
since the find search algorithm manually traverses type and method hierarchies and thereby implicitly supports annotation inheritance without a need for @Inherited
.
Author: Phillip Webb, Juergen Hoeller, Sam Brannen See Also: Since: 4.0
/**
* General utility methods for finding annotations, meta-annotations, and
* repeatable annotations on {@link AnnotatedElement AnnotatedElements}.
*
* <p>{@code AnnotatedElementUtils} defines the public API for Spring's
* meta-annotation programming model with support for <em>annotation attribute
* overrides</em>. If you do not need support for annotation attribute
* overrides, consider using {@link AnnotationUtils} instead.
*
* <p>Note that the features of this class are not provided by the JDK's
* introspection facilities themselves.
*
* <h3>Annotation Attribute Overrides</h3>
* <p>Support for meta-annotations with <em>attribute overrides</em> in
* <em>composed annotations</em> is provided by all variants of the
* {@code getMergedAnnotationAttributes()}, {@code getMergedAnnotation()},
* {@code getAllMergedAnnotations()}, {@code getMergedRepeatableAnnotations()},
* {@code findMergedAnnotationAttributes()}, {@code findMergedAnnotation()},
* {@code findAllMergedAnnotations()}, and {@code findMergedRepeatableAnnotations()}
* methods.
*
* <h3>Find vs. Get Semantics</h3>
* <p>The search algorithms used by methods in this class follow either
* <em>find</em> or <em>get</em> semantics. Consult the javadocs for each
* individual method for details on which search algorithm is used.
*
* <p><strong>Get semantics</strong> are limited to searching for annotations
* that are either <em>present</em> on an {@code AnnotatedElement} (i.e. declared
* locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared
* within the annotation hierarchy <em>above</em> the {@code AnnotatedElement}.
*
* <p><strong>Find semantics</strong> are much more exhaustive, providing
* <em>get semantics</em> plus support for the following:
*
* <ul>
* <li>Searching on interfaces, if the annotated element is a class
* <li>Searching on superclasses, if the annotated element is a class
* <li>Resolving bridged methods, if the annotated element is a method
* <li>Searching on methods in interfaces, if the annotated element is a method
* <li>Searching on methods in superclasses, if the annotated element is a method
* </ul>
*
* <h3>Support for {@code @Inherited}</h3>
* <p>Methods following <em>get semantics</em> will honor the contract of Java's
* {@link java.lang.annotation.Inherited @Inherited} annotation except that locally
* declared annotations (including custom composed annotations) will be favored over
* inherited annotations. In contrast, methods following <em>find semantics</em>
* will completely ignore the presence of {@code @Inherited} since the <em>find</em>
* search algorithm manually traverses type and method hierarchies and thereby
* implicitly supports annotation inheritance without a need for {@code @Inherited}.
*
* @author Phillip Webb
* @author Juergen Hoeller
* @author Sam Brannen
* @since 4.0
* @see AliasFor
* @see AnnotationAttributes
* @see AnnotationUtils
* @see BridgeMethodResolver
*/
public abstract class AnnotatedElementUtils {
null
constant used to denote that the search algorithm should continue. /**
* {@code null} constant used to denote that the search algorithm should continue.
*/
@Nullable
private static final Boolean CONTINUE = null;
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
private static final Processor<Boolean> alwaysTrueAnnotationProcessor = new AlwaysTrueBooleanAnnotationProcessor();
Build an adapted AnnotatedElement
for the given annotations, typically for use with other methods on AnnotatedElementUtils
. Params: - annotations – the annotations to expose through the
AnnotatedElement
Since: 4.3
/**
* Build an adapted {@link AnnotatedElement} for the given annotations,
* typically for use with other methods on {@link AnnotatedElementUtils}.
* @param annotations the annotations to expose through the {@code AnnotatedElement}
* @since 4.3
*/
public static AnnotatedElement forAnnotations(final Annotation... annotations) {
return new AnnotatedElement() {
@Override
@SuppressWarnings("unchecked")
@Nullable
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
for (Annotation ann : annotations) {
if (ann.annotationType() == annotationClass) {
return (T) ann;
}
}
return null;
}
@Override
public Annotation[] getAnnotations() {
return annotations;
}
@Override
public Annotation[] getDeclaredAnnotations() {
return annotations;
}
};
}
Get the fully qualified class names of all meta-annotation types
present on the annotation (of the specified annotationType
) on the supplied AnnotatedElement
. This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationType – the annotation type on which to find meta-annotations
See Also: Returns: the names of all meta-annotations present on the annotation, or null
if not found Since: 4.2
/**
* Get the fully qualified class names of all meta-annotation types
* <em>present</em> on the annotation (of the specified {@code annotationType})
* on the supplied {@link AnnotatedElement}.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationType the annotation type on which to find meta-annotations
* @return the names of all meta-annotations present on the annotation,
* or {@code null} if not found
* @since 4.2
* @see #getMetaAnnotationTypes(AnnotatedElement, String)
* @see #hasMetaAnnotationTypes
*/
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
return getMetaAnnotationTypes(element, element.getAnnotation(annotationType));
}
Get the fully qualified class names of all meta-annotation
types present on the annotation (of the specified annotationName
) on the supplied AnnotatedElement
. This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationName – the fully qualified class name of the annotation
type on which to find meta-annotations
See Also: Returns: the names of all meta-annotations present on the annotation,
or an empty set if none found
/**
* Get the fully qualified class names of all meta-annotation
* types <em>present</em> on the annotation (of the specified
* {@code annotationName}) on the supplied {@link AnnotatedElement}.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationName the fully qualified class name of the annotation
* type on which to find meta-annotations
* @return the names of all meta-annotations present on the annotation,
* or an empty set if none found
* @see #getMetaAnnotationTypes(AnnotatedElement, Class)
* @see #hasMetaAnnotationTypes
*/
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
return getMetaAnnotationTypes(element, AnnotationUtils.getAnnotation(element, annotationName));
}
private static Set<String> getMetaAnnotationTypes(AnnotatedElement element, @Nullable Annotation composed) {
if (composed == null) {
return Collections.emptySet();
}
try {
final Set<String> types = new LinkedHashSet<>();
searchWithGetSemantics(composed.annotationType(), Collections.emptySet(), null, null, new SimpleAnnotationProcessor<Object>(true) {
@Override
@Nullable
public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
types.add(annotation.annotationType().getName());
return CONTINUE;
}
}, new HashSet<>(), 1);
return types;
}
catch (Throwable ex) {
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
}
}
Determine if the supplied AnnotatedElement
is annotated with a composed annotation that is meta-annotated with an annotation of the specified annotationType
. This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationType – the meta-annotation type to find
See Also: Returns: true
if a matching meta-annotation is presentSince: 4.2.3
/**
* Determine if the supplied {@link AnnotatedElement} is annotated with
* a <em>composed annotation</em> that is meta-annotated with an
* annotation of the specified {@code annotationType}.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationType the meta-annotation type to find
* @return {@code true} if a matching meta-annotation is present
* @since 4.2.3
* @see #getMetaAnnotationTypes
*/
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
return hasMetaAnnotationTypes(element, annotationType, null);
}
Determine if the supplied AnnotatedElement
is annotated with a composed annotation that is meta-annotated with an annotation of the specified annotationName
. This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationName – the fully qualified class name of the
meta-annotation type to find
See Also: Returns: true
if a matching meta-annotation is present
/**
* Determine if the supplied {@link AnnotatedElement} is annotated with a
* <em>composed annotation</em> that is meta-annotated with an annotation
* of the specified {@code annotationName}.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationName the fully qualified class name of the
* meta-annotation type to find
* @return {@code true} if a matching meta-annotation is present
* @see #getMetaAnnotationTypes
*/
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
return hasMetaAnnotationTypes(element, null, annotationName);
}
private static boolean hasMetaAnnotationTypes(
AnnotatedElement element, @Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName) {
return Boolean.TRUE.equals(
searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() {
@Override
@Nullable
public Boolean process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
return (metaDepth > 0 ? Boolean.TRUE : CONTINUE);
}
}));
}
Determine if an annotation of the specified annotationType
is present on the supplied AnnotatedElement
or within the annotation hierarchy above the specified element.
If this method returns true
, then getMergedAnnotationAttributes
will return a non-null value.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationType – the annotation type to find
See Also: Returns: true
if a matching annotation is presentSince: 4.2.3
/**
* Determine if an annotation of the specified {@code annotationType}
* is <em>present</em> on the supplied {@link AnnotatedElement} or
* within the annotation hierarchy <em>above</em> the specified element.
* <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes}
* will return a non-null value.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationType the annotation type to find
* @return {@code true} if a matching annotation is present
* @since 4.2.3
* @see #hasAnnotation(AnnotatedElement, Class)
*/
public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType) {
// Shortcut: directly present on the element, with no processing needed?
if (element.isAnnotationPresent(annotationType)) {
return true;
}
return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor));
}
Determine if an annotation of the specified annotationName
is present on the supplied AnnotatedElement
or within the annotation hierarchy above the specified element.
If this method returns true
, then getMergedAnnotationAttributes
will return a non-null value.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationName – the fully qualified class name of the annotation type to find
Returns: true
if a matching annotation is present
/**
* Determine if an annotation of the specified {@code annotationName} is
* <em>present</em> on the supplied {@link AnnotatedElement} or within the
* annotation hierarchy <em>above</em> the specified element.
* <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes}
* will return a non-null value.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationName the fully qualified class name of the annotation type to find
* @return {@code true} if a matching annotation is present
*/
public static boolean isAnnotated(AnnotatedElement element, String annotationName) {
return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, alwaysTrueAnnotationProcessor));
}
Get the first annotation of the specified annotationType
within the annotation hierarchy above the supplied element
and merge that annotation's attributes with matching attributes from
annotations in lower levels of the annotation hierarchy.
@AliasFor
semantics are fully supported, both within a single annotation and within the annotation hierarchy.
This method delegates to getMergedAnnotationAttributes(AnnotatedElement, String)
.
Params: - element – the annotated element
- annotationType – the annotation type to find
See Also: Returns: the merged AnnotationAttributes
, or null
if not found Since: 4.2
/**
* Get the first annotation of the specified {@code annotationType} within
* the annotation hierarchy <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both
* within a single annotation and within the annotation hierarchy.
* <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}.
* @param element the annotated element
* @param annotationType the annotation type to find
* @return the merged {@code AnnotationAttributes}, or {@code null} if not found
* @since 4.2
* @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
* @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
* @see #getMergedAnnotation(AnnotatedElement, Class)
* @see #findMergedAnnotation(AnnotatedElement, Class)
*/
@Nullable
public static AnnotationAttributes getMergedAnnotationAttributes(
AnnotatedElement element, Class<? extends Annotation> annotationType) {
AnnotationAttributes attributes = searchWithGetSemantics(element, annotationType, null,
new MergedAnnotationAttributesProcessor());
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
return attributes;
}
Get the first annotation of the specified annotationName
within the annotation hierarchy above the supplied element
and merge that annotation's attributes with matching attributes from
annotations in lower levels of the annotation hierarchy.
@AliasFor
semantics are fully supported, both within a single annotation and within the annotation hierarchy.
This method delegates to getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
, supplying false
for classValuesAsString
and nestedAnnotationsAsMap
.
Params: - element – the annotated element
- annotationName – the fully qualified class name of the annotation type to find
See Also: Returns: the merged AnnotationAttributes
, or null
if not found Since: 4.2
/**
* Get the first annotation of the specified {@code annotationName} within
* the annotation hierarchy <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both
* within a single annotation and within the annotation hierarchy.
* <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)},
* supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}.
* @param element the annotated element
* @param annotationName the fully qualified class name of the annotation type to find
* @return the merged {@code AnnotationAttributes}, or {@code null} if not found
* @since 4.2
* @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
* @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
* @see #findMergedAnnotation(AnnotatedElement, Class)
* @see #getAllAnnotationAttributes(AnnotatedElement, String)
*/
@Nullable
public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName) {
return getMergedAnnotationAttributes(element, annotationName, false, false);
}
Get the first annotation of the specified annotationName
within the annotation hierarchy above the supplied element
and merge that annotation's attributes with matching attributes from
annotations in lower levels of the annotation hierarchy.
Attributes from lower levels in the annotation hierarchy override attributes of the same name from higher levels, and @AliasFor
semantics are fully supported, both within a single annotation and within the annotation hierarchy.
In contrast to getAllAnnotationAttributes
, the search algorithm used by this method will stop searching the annotation hierarchy once the first annotation of the specified annotationName
has been found. As a consequence, additional annotations of the specified annotationName
will be ignored.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationName – the fully qualified class name of the annotation type to find
- classValuesAsString – whether to convert Class references into Strings or to
preserve them as Class references
- nestedAnnotationsAsMap – whether to convert nested Annotation instances into
AnnotationAttributes
maps or to preserve them as Annotation instances
See Also: Returns: the merged AnnotationAttributes
, or null
if not found Since: 4.2
/**
* Get the first annotation of the specified {@code annotationName} within
* the annotation hierarchy <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
* <p>Attributes from lower levels in the annotation hierarchy override attributes
* of the same name from higher levels, and {@link AliasFor @AliasFor} semantics are
* fully supported, both within a single annotation and within the annotation hierarchy.
* <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm used by
* this method will stop searching the annotation hierarchy once the first annotation
* of the specified {@code annotationName} has been found. As a consequence,
* additional annotations of the specified {@code annotationName} will be ignored.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationName the fully qualified class name of the annotation type to find
* @param classValuesAsString whether to convert Class references into Strings or to
* preserve them as Class references
* @param nestedAnnotationsAsMap whether to convert nested Annotation instances
* into {@code AnnotationAttributes} maps or to preserve them as Annotation instances
* @return the merged {@code AnnotationAttributes}, or {@code null} if not found
* @since 4.2
* @see #findMergedAnnotation(AnnotatedElement, Class)
* @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
* @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
@Nullable
public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element,
String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName,
new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
return attributes;
}
Get the first annotation of the specified annotationType
within the annotation hierarchy above the supplied element
, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy, and synthesize the result back into an annotation of the specified annotationType
. @AliasFor
semantics are fully supported, both within a single annotation and within the annotation hierarchy.
This method delegates to getMergedAnnotationAttributes(AnnotatedElement, Class<? extends Annotation>)
and AnnotationUtils.synthesizeAnnotation(Map<String,Object>, Class<Annotation>, AnnotatedElement)
.
Params: - element – the annotated element
- annotationType – the annotation type to find
See Also: Returns: the merged, synthesized Annotation
, or null
if not found Since: 4.2
/**
* Get the first annotation of the specified {@code annotationType} within
* the annotation hierarchy <em>above</em> the supplied {@code element},
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy, and synthesize
* the result back into an annotation of the specified {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both
* within a single annotation and within the annotation hierarchy.
* <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, Class)}
* and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}.
* @param element the annotated element
* @param annotationType the annotation type to find
* @return the merged, synthesized {@code Annotation}, or {@code null} if not found
* @since 4.2
* @see #getMergedAnnotationAttributes(AnnotatedElement, Class)
* @see #findMergedAnnotation(AnnotatedElement, Class)
* @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)
*/
@Nullable
public static <A extends Annotation> A getMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
// Shortcut: directly present on the element, with no merging needed?
A annotation = element.getDeclaredAnnotation(annotationType);
if (annotation != null) {
return AnnotationUtils.synthesizeAnnotation(annotation, element);
}
// Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types...
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) {
return null;
}
// Exhaustive retrieval of merged annotation attributes...
AnnotationAttributes attributes = getMergedAnnotationAttributes(element, annotationType);
return (attributes != null ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null);
}
Get all annotations of the specified annotationType
within the annotation hierarchy above the supplied element
; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType
. @AliasFor
semantics are fully supported, both within a single annotation and within annotation hierarchies.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element (never
null
) - annotationType – the annotation type to find (never
null
)
See Also: Returns: the set of all merged, synthesized Annotations
found, or an empty set if none were found Since: 4.3
/**
* Get <strong>all</strong> annotations of the specified {@code annotationType}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the annotation
* hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find (never {@code null})
* @return the set of all merged, synthesized {@code Annotations} found,
* or an empty set if none were found
* @since 4.3
* @see #getMergedAnnotation(AnnotatedElement, Class)
* @see #getAllAnnotationAttributes(AnnotatedElement, String)
* @see #findAllMergedAnnotations(AnnotatedElement, Class)
*/
public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithGetSemantics(element, annotationType, null, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
Get all annotations of the specified annotationTypes
within the annotation hierarchy above the supplied element
; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the corresponding annotationType
. @AliasFor
semantics are fully supported, both within a single annotation and within annotation hierarchies.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element (never
null
) - annotationTypes – the annotation types to find
See Also: Returns: the set of all merged, synthesized Annotations
found, or an empty set if none were found Since: 5.1
/**
* Get <strong>all</strong> annotations of the specified {@code annotationTypes}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the
* annotation hierarchy and synthesize the results back into an annotation
* of the corresponding {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationTypes the annotation types to find
* @return the set of all merged, synthesized {@code Annotations} found,
* or an empty set if none were found
* @since 5.1
* @see #getAllMergedAnnotations(AnnotatedElement, Class)
*/
public static Set<Annotation> getAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) {
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithGetSemantics(element, annotationTypes, null, null, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
Get all repeatable annotations of the specified annotationType
within the annotation hierarchy above the supplied element
; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType
. The container type that holds the repeatable annotations will be looked up via Repeatable
.
@AliasFor
semantics are fully supported, both within a single annotation and within annotation hierarchies.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element (never
null
) - annotationType – the annotation type to find (never
null
)
Throws: - IllegalArgumentException – if the
element
or annotationType
is null
, or if the container type cannot be resolved
See Also: Returns: the set of all merged repeatable Annotations
found, or an empty set if none were found Since: 4.3
/**
* Get all <em>repeatable annotations</em> of the specified {@code annotationType}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the annotation
* hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* <p>The container type that holds the repeatable annotations will be looked up
* via {@link java.lang.annotation.Repeatable}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find (never {@code null})
* @return the set of all merged repeatable {@code Annotations} found,
* or an empty set if none were found
* @throws IllegalArgumentException if the {@code element} or {@code annotationType}
* is {@code null}, or if the container type cannot be resolved
* @since 4.3
* @see #getMergedAnnotation(AnnotatedElement, Class)
* @see #getAllMergedAnnotations(AnnotatedElement, Class)
* @see #getMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
*/
public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(AnnotatedElement element,
Class<A> annotationType) {
return getMergedRepeatableAnnotations(element, annotationType, null);
}
Get all repeatable annotations of the specified annotationType
within the annotation hierarchy above the supplied element
; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType
. @AliasFor
semantics are fully supported, both within a single annotation and within annotation hierarchies.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element (never
null
) - annotationType – the annotation type to find (never
null
) - containerType – the type of the container that holds the annotations; may be
null
if the container type should be looked up via Repeatable
Throws: - IllegalArgumentException – if the
element
or annotationType
is null
, or if the container type cannot be resolved - AnnotationConfigurationException – if the supplied
containerType
is not a valid container annotation for the supplied annotationType
See Also: Returns: the set of all merged repeatable Annotations
found, or an empty set if none were found Since: 4.3
/**
* Get all <em>repeatable annotations</em> of the specified {@code annotationType}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the annotation
* hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find (never {@code null})
* @param containerType the type of the container that holds the annotations;
* may be {@code null} if the container type should be looked up via
* {@link java.lang.annotation.Repeatable}
* @return the set of all merged repeatable {@code Annotations} found,
* or an empty set if none were found
* @throws IllegalArgumentException if the {@code element} or {@code annotationType}
* is {@code null}, or if the container type cannot be resolved
* @throws AnnotationConfigurationException if the supplied {@code containerType}
* is not a valid container annotation for the supplied {@code annotationType}
* @since 4.3
* @see #getMergedAnnotation(AnnotatedElement, Class)
* @see #getAllMergedAnnotations(AnnotatedElement, Class)
*/
public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(AnnotatedElement element,
Class<A> annotationType, @Nullable Class<? extends Annotation> containerType) {
if (containerType == null) {
containerType = resolveContainerType(annotationType);
}
else {
validateContainerType(annotationType, containerType);
}
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithGetSemantics(element, Collections.singleton(annotationType), null, containerType, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
Get the annotation attributes of all annotations of the specified annotationName
in the annotation hierarchy above the supplied AnnotatedElement
and store the results in a MultiValueMap
. Note: in contrast to getMergedAnnotationAttributes(AnnotatedElement, String)
, this method does not support attribute overrides.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationName – the fully qualified class name of the annotation type to find
See Also: Returns: a MultiValueMap
keyed by attribute name, containing the annotation attributes from all annotations found, or null
if not found
/**
* Get the annotation attributes of <strong>all</strong> annotations of the specified
* {@code annotationName} in the annotation hierarchy above the supplied
* {@link AnnotatedElement} and store the results in a {@link MultiValueMap}.
* <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)},
* this method does <em>not</em> support attribute overrides.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationName the fully qualified class name of the annotation type to find
* @return a {@link MultiValueMap} keyed by attribute name, containing the annotation
* attributes from all annotations found, or {@code null} if not found
* @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
@Nullable
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationName) {
return getAllAnnotationAttributes(element, annotationName, false, false);
}
Get the annotation attributes of all annotations of the specified annotationName
in the annotation hierarchy above the supplied AnnotatedElement
and store the results in a MultiValueMap
. Note: in contrast to getMergedAnnotationAttributes(AnnotatedElement, String)
, this method does not support attribute overrides.
This method follows get semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationName – the fully qualified class name of the annotation type to find
- classValuesAsString – whether to convert Class references into Strings or to
preserve them as Class references
- nestedAnnotationsAsMap – whether to convert nested Annotation instances into
AnnotationAttributes
maps or to preserve them as Annotation instances
Returns: a MultiValueMap
keyed by attribute name, containing the annotation attributes from all annotations found, or null
if not found
/**
* Get the annotation attributes of <strong>all</strong> annotations of
* the specified {@code annotationName} in the annotation hierarchy above
* the supplied {@link AnnotatedElement} and store the results in a
* {@link MultiValueMap}.
* <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)},
* this method does <em>not</em> support attribute overrides.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationName the fully qualified class name of the annotation type to find
* @param classValuesAsString whether to convert Class references into Strings or to
* preserve them as Class references
* @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
* {@code AnnotationAttributes} maps or to preserve them as Annotation instances
* @return a {@link MultiValueMap} keyed by attribute name, containing the annotation
* attributes from all annotations found, or {@code null} if not found
*/
@Nullable
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
final MultiValueMap<String, Object> attributesMap = new LinkedMultiValueMap<>();
searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor<Object>() {
@Override
@Nullable
public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes(
annotation, classValuesAsString, nestedAnnotationsAsMap);
annotationAttributes.forEach(attributesMap::add);
return CONTINUE;
}
});
return (!attributesMap.isEmpty() ? attributesMap : null);
}
Determine if an annotation of the specified annotationType
is available on the supplied AnnotatedElement
or within the annotation hierarchy above the specified element.
If this method returns true
, then findMergedAnnotationAttributes
will return a non-null value.
This method follows find semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationType – the annotation type to find
See Also: Returns: true
if a matching annotation is presentSince: 4.3
/**
* Determine if an annotation of the specified {@code annotationType}
* is <em>available</em> on the supplied {@link AnnotatedElement} or
* within the annotation hierarchy <em>above</em> the specified element.
* <p>If this method returns {@code true}, then {@link #findMergedAnnotationAttributes}
* will return a non-null value.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationType the annotation type to find
* @return {@code true} if a matching annotation is present
* @since 4.3
* @see #isAnnotated(AnnotatedElement, Class)
*/
public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) {
// Shortcut: directly present on the element, with no processing needed?
if (element.isAnnotationPresent(annotationType)) {
return true;
}
return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor));
}
Find the first annotation of the specified annotationType
within the annotation hierarchy above the supplied element
and merge that annotation's attributes with matching attributes from
annotations in lower levels of the annotation hierarchy.
Attributes from lower levels in the annotation hierarchy override attributes of the same name from higher levels, and @AliasFor
semantics are fully supported, both within a single annotation and within the annotation hierarchy.
In contrast to getAllAnnotationAttributes
, the search algorithm used by this method will stop searching the annotation hierarchy once the first annotation of the specified annotationType
has been found. As a consequence, additional annotations of the specified annotationType
will be ignored.
This method follows find semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationType – the annotation type to find
- classValuesAsString – whether to convert Class references into
Strings or to preserve them as Class references
- nestedAnnotationsAsMap – whether to convert nested Annotation instances into
AnnotationAttributes
maps or to preserve them as Annotation instances
See Also: Returns: the merged AnnotationAttributes
, or null
if not found Since: 4.2
/**
* Find the first annotation of the specified {@code annotationType} within
* the annotation hierarchy <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
* <p>Attributes from lower levels in the annotation hierarchy override
* attributes of the same name from higher levels, and
* {@link AliasFor @AliasFor} semantics are fully supported, both
* within a single annotation and within the annotation hierarchy.
* <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm
* used by this method will stop searching the annotation hierarchy once the
* first annotation of the specified {@code annotationType} has been found.
* As a consequence, additional annotations of the specified
* {@code annotationType} will be ignored.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationType the annotation type to find
* @param classValuesAsString whether to convert Class references into
* Strings or to preserve them as Class references
* @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
* {@code AnnotationAttributes} maps or to preserve them as Annotation instances
* @return the merged {@code AnnotationAttributes}, or {@code null} if not found
* @since 4.2
* @see #findMergedAnnotation(AnnotatedElement, Class)
* @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
@Nullable
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
AnnotationAttributes attributes = searchWithFindSemantics(element, annotationType, null,
new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
return attributes;
}
Find the first annotation of the specified annotationName
within the annotation hierarchy above the supplied element
and merge that annotation's attributes with matching attributes from
annotations in lower levels of the annotation hierarchy.
Attributes from lower levels in the annotation hierarchy override attributes of the same name from higher levels, and @AliasFor
semantics are fully supported, both within a single annotation and within the annotation hierarchy.
In contrast to getAllAnnotationAttributes
, the search algorithm used by this method will stop searching the annotation hierarchy once the first annotation of the specified annotationName
has been found. As a consequence, additional annotations of the specified annotationName
will be ignored.
This method follows find semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationName – the fully qualified class name of the annotation type to find
- classValuesAsString – whether to convert Class references into Strings or to
preserve them as Class references
- nestedAnnotationsAsMap – whether to convert nested Annotation instances into
AnnotationAttributes
maps or to preserve them as Annotation instances
See Also: Returns: the merged AnnotationAttributes
, or null
if not found Since: 4.2
/**
* Find the first annotation of the specified {@code annotationName} within
* the annotation hierarchy <em>above</em> the supplied {@code element} and
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy.
* <p>Attributes from lower levels in the annotation hierarchy override
* attributes of the same name from higher levels, and
* {@link AliasFor @AliasFor} semantics are fully supported, both
* within a single annotation and within the annotation hierarchy.
* <p>In contrast to {@link #getAllAnnotationAttributes}, the search
* algorithm used by this method will stop searching the annotation
* hierarchy once the first annotation of the specified
* {@code annotationName} has been found. As a consequence, additional
* annotations of the specified {@code annotationName} will be ignored.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationName the fully qualified class name of the annotation type to find
* @param classValuesAsString whether to convert Class references into Strings or to
* preserve them as Class references
* @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
* {@code AnnotationAttributes} maps or to preserve them as Annotation instances
* @return the merged {@code AnnotationAttributes}, or {@code null} if not found
* @since 4.2
* @see #findMergedAnnotation(AnnotatedElement, Class)
* @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
@Nullable
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
AnnotationAttributes attributes = searchWithFindSemantics(element, null, annotationName,
new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
return attributes;
}
Find the first annotation of the specified annotationType
within the annotation hierarchy above the supplied element
, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy, and synthesize the result back into an annotation of the specified annotationType
. @AliasFor
semantics are fully supported, both within a single annotation and within the annotation hierarchy.
This method follows find semantics as described in the class-level javadoc.
Params: - element – the annotated element
- annotationType – the annotation type to find
See Also: Returns: the merged, synthesized Annotation
, or null
if not found Since: 4.2
/**
* Find the first annotation of the specified {@code annotationType} within
* the annotation hierarchy <em>above</em> the supplied {@code element},
* merge that annotation's attributes with <em>matching</em> attributes from
* annotations in lower levels of the annotation hierarchy, and synthesize
* the result back into an annotation of the specified {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both
* within a single annotation and within the annotation hierarchy.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element
* @param annotationType the annotation type to find
* @return the merged, synthesized {@code Annotation}, or {@code null} if not found
* @since 4.2
* @see #findAllMergedAnnotations(AnnotatedElement, Class)
* @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
* @see #getMergedAnnotationAttributes(AnnotatedElement, Class)
*/
@Nullable
public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
// Shortcut: directly present on the element, with no merging needed?
A annotation = element.getDeclaredAnnotation(annotationType);
if (annotation != null) {
return AnnotationUtils.synthesizeAnnotation(annotation, element);
}
// Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types...
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) {
return null;
}
// Exhaustive retrieval of merged annotation attributes...
AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationType, false, false);
return (attributes != null ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null);
}
Find all annotations of the specified annotationType
within the annotation hierarchy above the supplied element
; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType
. @AliasFor
semantics are fully supported, both within a single annotation and within annotation hierarchies.
This method follows find semantics as described in the class-level javadoc.
Params: - element – the annotated element (never
null
) - annotationType – the annotation type to find (never
null
)
See Also: Returns: the set of all merged, synthesized Annotations
found, or an empty set if none were found Since: 4.3
/**
* Find <strong>all</strong> annotations of the specified {@code annotationType}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the annotation
* hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find (never {@code null})
* @return the set of all merged, synthesized {@code Annotations} found,
* or an empty set if none were found
* @since 4.3
* @see #findMergedAnnotation(AnnotatedElement, Class)
* @see #getAllMergedAnnotations(AnnotatedElement, Class)
*/
public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithFindSemantics(element, annotationType, null, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
Find all annotations of the specified annotationTypes
within the annotation hierarchy above the supplied element
; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the corresponding annotationType
. @AliasFor
semantics are fully supported, both within a single annotation and within annotation hierarchies.
This method follows find semantics as described in the class-level javadoc.
Params: - element – the annotated element (never
null
) - annotationTypes – the annotation types to find
See Also: Returns: the set of all merged, synthesized Annotations
found, or an empty set if none were found Since: 5.1
/**
* Find <strong>all</strong> annotations of the specified {@code annotationTypes}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the
* annotation hierarchy and synthesize the results back into an annotation
* of the corresponding {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationTypes the annotation types to find
* @return the set of all merged, synthesized {@code Annotations} found,
* or an empty set if none were found
* @since 5.1
* @see #findAllMergedAnnotations(AnnotatedElement, Class)
*/
public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) {
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithFindSemantics(element, annotationTypes, null, null, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
Find all repeatable annotations of the specified annotationType
within the annotation hierarchy above the supplied element
; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType
. The container type that holds the repeatable annotations will be looked up via Repeatable
.
@AliasFor
semantics are fully supported, both within a single annotation and within annotation hierarchies.
This method follows find semantics as described in the class-level javadoc.
Params: - element – the annotated element (never
null
) - annotationType – the annotation type to find (never
null
)
Throws: - IllegalArgumentException – if the
element
or annotationType
is null
, or if the container type cannot be resolved
See Also: Returns: the set of all merged repeatable Annotations
found, or an empty set if none were found Since: 4.3
/**
* Find all <em>repeatable annotations</em> of the specified {@code annotationType}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the annotation
* hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* <p>The container type that holds the repeatable annotations will be looked up
* via {@link java.lang.annotation.Repeatable}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find (never {@code null})
* @return the set of all merged repeatable {@code Annotations} found,
* or an empty set if none were found
* @throws IllegalArgumentException if the {@code element} or {@code annotationType}
* is {@code null}, or if the container type cannot be resolved
* @since 4.3
* @see #findMergedAnnotation(AnnotatedElement, Class)
* @see #findAllMergedAnnotations(AnnotatedElement, Class)
* @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
*/
public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element,
Class<A> annotationType) {
return findMergedRepeatableAnnotations(element, annotationType, null);
}
Find all repeatable annotations of the specified annotationType
within the annotation hierarchy above the supplied element
; and for each annotation found, merge that annotation's attributes with matching attributes from annotations in lower levels of the annotation hierarchy and synthesize the results back into an annotation of the specified annotationType
. @AliasFor
semantics are fully supported, both within a single annotation and within annotation hierarchies.
This method follows find semantics as described in the class-level javadoc.
Params: - element – the annotated element (never
null
) - annotationType – the annotation type to find (never
null
) - containerType – the type of the container that holds the annotations; may be
null
if the container type should be looked up via Repeatable
Throws: - IllegalArgumentException – if the
element
or annotationType
is null
, or if the container type cannot be resolved - AnnotationConfigurationException – if the supplied
containerType
is not a valid container annotation for the supplied annotationType
See Also: Returns: the set of all merged repeatable Annotations
found, or an empty set if none were found Since: 4.3
/**
* Find all <em>repeatable annotations</em> of the specified {@code annotationType}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the annotation
* hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find (never {@code null})
* @param containerType the type of the container that holds the annotations;
* may be {@code null} if the container type should be looked up via
* {@link java.lang.annotation.Repeatable}
* @return the set of all merged repeatable {@code Annotations} found,
* or an empty set if none were found
* @throws IllegalArgumentException if the {@code element} or {@code annotationType}
* is {@code null}, or if the container type cannot be resolved
* @throws AnnotationConfigurationException if the supplied {@code containerType}
* is not a valid container annotation for the supplied {@code annotationType}
* @since 4.3
* @see #findMergedAnnotation(AnnotatedElement, Class)
* @see #findAllMergedAnnotations(AnnotatedElement, Class)
*/
public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element,
Class<A> annotationType, @Nullable Class<? extends Annotation> containerType) {
if (containerType == null) {
containerType = resolveContainerType(annotationType);
}
else {
validateContainerType(annotationType, containerType);
}
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
searchWithFindSemantics(element, Collections.singleton(annotationType), null, containerType, processor);
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
}
Search for annotations of the specified annotationName
or annotationType
on the specified element
, following get semantics.
Params: - element – the annotated element
- annotationType – the annotation type to find
- annotationName – the fully qualified class name of the annotation type to find (as an alternative to
annotationType
) - processor – the processor to delegate to
Returns: the result of the processor (potentially null
)
/**
* Search for annotations of the specified {@code annotationName} or
* {@code annotationType} on the specified {@code element}, following
* <em>get semantics</em>.
* @param element the annotated element
* @param annotationType the annotation type to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param processor the processor to delegate to
* @return the result of the processor (potentially {@code null})
*/
@Nullable
private static <T> T searchWithGetSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType,
@Nullable String annotationName, Processor<T> processor) {
return searchWithGetSemantics(element,
(annotationType != null ? Collections.singleton(annotationType) : Collections.emptySet()),
annotationName, null, processor);
}
Search for annotations of the specified annotationName
or annotationType
on the specified element
, following get semantics.
Params: - element – the annotated element
- annotationTypes – the annotation types to find
- annotationName – the fully qualified class name of the annotation type to find (as an alternative to
annotationType
) - containerType – the type of the container that holds repeatable annotations, or
null
if the annotation is not repeatable - processor – the processor to delegate to
Returns: the result of the processor (potentially null
) Since: 4.3
/**
* Search for annotations of the specified {@code annotationName} or
* {@code annotationType} on the specified {@code element}, following
* <em>get semantics</em>.
* @param element the annotated element
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
* annotations, or {@code null} if the annotation is not repeatable
* @param processor the processor to delegate to
* @return the result of the processor (potentially {@code null})
* @since 4.3
*/
@Nullable
private static <T> T searchWithGetSemantics(AnnotatedElement element,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor) {
try {
return searchWithGetSemantics(element, annotationTypes, annotationName, containerType, processor,
new HashSet<>(), 0);
}
catch (Throwable ex) {
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
}
}
Perform the search algorithm for the searchWithGetSemantics
method, avoiding endless recursion by tracking which annotated elements have already been visited.
The metaDepth
parameter is explained in the process()
method of the Processor
API.
Params: - element – the annotated element
- annotationTypes – the annotation types to find
- annotationName – the fully qualified class name of the annotation type to find (as an alternative to
annotationType
) - containerType – the type of the container that holds repeatable annotations, or
null
if the annotation is not repeatable - processor – the processor to delegate to
- visited – the set of annotated elements that have already been visited
- metaDepth – the meta-depth of the annotation
Returns: the result of the processor (potentially null
)
/**
* Perform the search algorithm for the {@link #searchWithGetSemantics}
* method, avoiding endless recursion by tracking which annotated elements
* have already been <em>visited</em>.
* <p>The {@code metaDepth} parameter is explained in the
* {@link Processor#process process()} method of the {@link Processor} API.
* @param element the annotated element
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
* annotations, or {@code null} if the annotation is not repeatable
* @param processor the processor to delegate to
* @param visited the set of annotated elements that have already been visited
* @param metaDepth the meta-depth of the annotation
* @return the result of the processor (potentially {@code null})
*/
@Nullable
private static <T> T searchWithGetSemantics(AnnotatedElement element,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> visited, int metaDepth) {
if (visited.add(element)) {
try {
// Start searching within locally declared annotations
List<Annotation> declaredAnnotations = Arrays.asList(AnnotationUtils.getDeclaredAnnotations(element));
T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations,
annotationTypes, annotationName, containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new
Class<?> superclass = ((Class<?>) element).getSuperclass();
if (superclass != null && superclass != Object.class) {
List<Annotation> inheritedAnnotations = new LinkedList<>();
for (Annotation annotation : element.getAnnotations()) {
if (!declaredAnnotations.contains(annotation)) {
inheritedAnnotations.add(annotation);
}
}
// Continue searching within inherited annotations
result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations,
annotationTypes, annotationName, containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
catch (Throwable ex) {
AnnotationUtils.handleIntrospectionFailure(element, ex);
}
}
return null;
}
This method is invoked by searchWithGetSemantics
to perform the actual search within the supplied list of annotations. This method should be invoked first with locally declared annotations
and then subsequently with inherited annotations, thereby allowing
local annotations to take precedence over inherited annotations.
The metaDepth
parameter is explained in the process()
method of the Processor
API.
Params: - element – the element that is annotated with the supplied annotations, used for contextual logging; may be
null
if unknown - annotations – the annotations to search in
- annotationTypes – the annotation types to find
- annotationName – the fully qualified class name of the annotation type to find (as an alternative to
annotationType
) - containerType – the type of the container that holds repeatable annotations, or
null
if the annotation is not repeatable - processor – the processor to delegate to
- visited – the set of annotated elements that have already been visited
- metaDepth – the meta-depth of the annotation
Returns: the result of the processor (potentially null
) Since: 4.2
/**
* This method is invoked by {@link #searchWithGetSemantics} to perform
* the actual search within the supplied list of annotations.
* <p>This method should be invoked first with locally declared annotations
* and then subsequently with inherited annotations, thereby allowing
* local annotations to take precedence over inherited annotations.
* <p>The {@code metaDepth} parameter is explained in the
* {@link Processor#process process()} method of the {@link Processor} API.
* @param element the element that is annotated with the supplied
* annotations, used for contextual logging; may be {@code null} if unknown
* @param annotations the annotations to search in
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
* annotations, or {@code null} if the annotation is not repeatable
* @param processor the processor to delegate to
* @param visited the set of annotated elements that have already been visited
* @param metaDepth the meta-depth of the annotation
* @return the result of the processor (potentially {@code null})
* @since 4.2
*/
@Nullable
private static <T> T searchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element,
List<Annotation> annotations, Set<Class<? extends Annotation>> annotationTypes,
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
// Search in annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
if (annotationTypes.contains(currentAnnotationType) ||
currentAnnotationType.getName().equals(annotationName) ||
processor.alwaysProcesses()) {
T result = processor.process(element, annotation, metaDepth);
if (result != null) {
if (processor.aggregates() && metaDepth == 0) {
processor.getAggregatedResults().add(result);
}
else {
return result;
}
}
}
// Repeatable annotations in container?
else if (currentAnnotationType == containerType) {
for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
T result = processor.process(element, contained, metaDepth);
if (result != null) {
// No need to post-process since repeatable annotations within a
// container cannot be composed annotations.
processor.getAggregatedResults().add(result);
}
}
}
}
}
// Recursively search in meta-annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
T result = searchWithGetSemantics(currentAnnotationType, annotationTypes,
annotationName, containerType, processor, visited, metaDepth + 1);
if (result != null) {
processor.postProcess(element, annotation, result);
if (processor.aggregates() && metaDepth == 0) {
processor.getAggregatedResults().add(result);
}
else {
return result;
}
}
}
}
return null;
}
Search for annotations of the specified annotationName
or annotationType
on the specified element
, following find semantics.
Params: - element – the annotated element
- annotationType – the annotation type to find
- annotationName – the fully qualified class name of the annotation type to find (as an alternative to
annotationType
) - processor – the processor to delegate to
Returns: the result of the processor (potentially null
) Since: 4.2
/**
* Search for annotations of the specified {@code annotationName} or
* {@code annotationType} on the specified {@code element}, following
* <em>find semantics</em>.
* @param element the annotated element
* @param annotationType the annotation type to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param processor the processor to delegate to
* @return the result of the processor (potentially {@code null})
* @since 4.2
*/
@Nullable
private static <T> T searchWithFindSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType,
@Nullable String annotationName, Processor<T> processor) {
return searchWithFindSemantics(element,
(annotationType != null ? Collections.singleton(annotationType) : Collections.emptySet()),
annotationName, null, processor);
}
Search for annotations of the specified annotationName
or annotationType
on the specified element
, following find semantics.
Params: - element – the annotated element
- annotationTypes – the annotation types to find
- annotationName – the fully qualified class name of the annotation type to find (as an alternative to
annotationType
) - containerType – the type of the container that holds repeatable annotations, or
null
if the annotation is not repeatable - processor – the processor to delegate to
Returns: the result of the processor (potentially null
) Since: 4.3
/**
* Search for annotations of the specified {@code annotationName} or
* {@code annotationType} on the specified {@code element}, following
* <em>find semantics</em>.
* @param element the annotated element
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
* annotations, or {@code null} if the annotation is not repeatable
* @param processor the processor to delegate to
* @return the result of the processor (potentially {@code null})
* @since 4.3
*/
@Nullable
private static <T> T searchWithFindSemantics(AnnotatedElement element,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor) {
if (containerType != null && !processor.aggregates()) {
throw new IllegalArgumentException(
"Searches for repeatable annotations must supply an aggregating Processor");
}
try {
return searchWithFindSemantics(
element, annotationTypes, annotationName, containerType, processor, new HashSet<>(), 0);
}
catch (Throwable ex) {
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
}
}
Perform the search algorithm for the searchWithFindSemantics
method, avoiding endless recursion by tracking which annotated elements have already been visited.
The metaDepth
parameter is explained in the process()
method of the Processor
API.
Params: - element – the annotated element (never
null
) - annotationTypes – the annotation types to find
- annotationName – the fully qualified class name of the annotation type to find (as an alternative to
annotationType
) - containerType – the type of the container that holds repeatable annotations, or
null
if the annotation is not repeatable - processor – the processor to delegate to
- visited – the set of annotated elements that have already been visited
- metaDepth – the meta-depth of the annotation
Returns: the result of the processor (potentially null
) Since: 4.2
/**
* Perform the search algorithm for the {@link #searchWithFindSemantics}
* method, avoiding endless recursion by tracking which annotated elements
* have already been <em>visited</em>.
* <p>The {@code metaDepth} parameter is explained in the
* {@link Processor#process process()} method of the {@link Processor} API.
* @param element the annotated element (never {@code null})
* @param annotationTypes the annotation types to find
* @param annotationName the fully qualified class name of the annotation
* type to find (as an alternative to {@code annotationType})
* @param containerType the type of the container that holds repeatable
* annotations, or {@code null} if the annotation is not repeatable
* @param processor the processor to delegate to
* @param visited the set of annotated elements that have already been visited
* @param metaDepth the meta-depth of the annotation
* @return the result of the processor (potentially {@code null})
* @since 4.2
*/
@Nullable
private static <T> T searchWithFindSemantics(AnnotatedElement element,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> visited, int metaDepth) {
if (visited.add(element)) {
try {
// Locally declared annotations (ignoring @Inherited)
Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(element);
if (annotations.length > 0) {
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);
// Search in local annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
if (annotationTypes.contains(currentAnnotationType) ||
currentAnnotationType.getName().equals(annotationName) ||
processor.alwaysProcesses()) {
T result = processor.process(element, annotation, metaDepth);
if (result != null) {
if (aggregatedResults != null && metaDepth == 0) {
aggregatedResults.add(result);
}
else {
return result;
}
}
}
// Repeatable annotations in container?
else if (currentAnnotationType == containerType) {
for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
T result = processor.process(element, contained, metaDepth);
if (aggregatedResults != null && result != null) {
// No need to post-process since repeatable annotations within a
// container cannot be composed annotations.
aggregatedResults.add(result);
}
}
}
}
}
// Recursively search in meta-annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
T result = searchWithFindSemantics(currentAnnotationType, annotationTypes, annotationName,
containerType, processor, visited, metaDepth + 1);
if (result != null) {
processor.postProcess(currentAnnotationType, annotation, result);
if (aggregatedResults != null && metaDepth == 0) {
aggregatedResults.add(result);
}
else {
return result;
}
}
}
}
if (!CollectionUtils.isEmpty(aggregatedResults)) {
// Prepend to support top-down ordering within class hierarchies
processor.getAggregatedResults().addAll(0, aggregatedResults);
}
}
if (element instanceof Method) {
Method method = (Method) element;
T result;
// Search on possibly bridged method
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (resolvedMethod != method) {
result = searchWithFindSemantics(resolvedMethod, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
// Search on methods in interfaces declared locally
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
if (ifcs.length > 0) {
result = searchOnInterfaces(method, annotationTypes, annotationName,
containerType, processor, visited, metaDepth, ifcs);
if (result != null) {
return result;
}
}
// Search on methods in class hierarchy and interface hierarchy
Class<?> clazz = method.getDeclaringClass();
while (true) {
clazz = clazz.getSuperclass();
if (clazz == null || clazz == Object.class) {
break;
}
Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
if (!annotatedMethods.isEmpty()) {
for (Method annotatedMethod : annotatedMethods) {
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod);
result = searchWithFindSemantics(resolvedSuperMethod, annotationTypes,
annotationName, containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
// Search on interfaces declared on superclass
result = searchOnInterfaces(method, annotationTypes, annotationName,
containerType, processor, visited, metaDepth, clazz.getInterfaces());
if (result != null) {
return result;
}
}
}
else if (element instanceof Class) {
Class<?> clazz = (Class<?>) element;
if (!Annotation.class.isAssignableFrom(clazz)) {
// Search on interfaces
for (Class<?> ifc : clazz.getInterfaces()) {
T result = searchWithFindSemantics(ifc, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
// Search on superclass
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && superclass != Object.class) {
T result = searchWithFindSemantics(superclass, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
}
catch (Throwable ex) {
AnnotationUtils.handleIntrospectionFailure(element, ex);
}
}
return null;
}
@Nullable
private static <T> T searchOnInterfaces(Method method, Set<Class<? extends Annotation>> annotationTypes,
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
for (Class<?> ifc : ifcs) {
Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(ifc);
if (!annotatedMethods.isEmpty()) {
for (Method annotatedMethod : annotatedMethods) {
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
T result = searchWithFindSemantics(annotatedMethod, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
}
return null;
}
Get the array of raw (unsynthesized) annotations from the value
attribute of the supplied repeatable annotation container
. Since: 4.3
/**
* Get the array of raw (unsynthesized) annotations from the {@code value}
* attribute of the supplied repeatable annotation {@code container}.
* @since 4.3
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> A[] getRawAnnotationsFromContainer(
@Nullable AnnotatedElement element, Annotation container) {
try {
A[] value = (A[]) AnnotationUtils.getValue(container);
if (value != null) {
return value;
}
}
catch (Throwable ex) {
AnnotationUtils.handleIntrospectionFailure(element, ex);
}
// Unable to read value from repeating annotation container -> ignore it.
return (A[]) EMPTY_ANNOTATION_ARRAY;
}
Resolve the container type for the supplied repeatable annotationType
. Delegates to AnnotationUtils.resolveContainerAnnotationType(Class<? extends Annotation>)
.
Params: - annotationType – the annotation type to resolve the container for
Throws: - IllegalArgumentException – if the container type cannot be resolved
Returns: the container type (never null
) Since: 4.3
/**
* Resolve the container type for the supplied repeatable {@code annotationType}.
* <p>Delegates to {@link AnnotationUtils#resolveContainerAnnotationType(Class)}.
* @param annotationType the annotation type to resolve the container for
* @return the container type (never {@code null})
* @throws IllegalArgumentException if the container type cannot be resolved
* @since 4.3
*/
private static Class<? extends Annotation> resolveContainerType(Class<? extends Annotation> annotationType) {
Class<? extends Annotation> containerType = AnnotationUtils.resolveContainerAnnotationType(annotationType);
if (containerType == null) {
throw new IllegalArgumentException(
"Annotation type must be a repeatable annotation: failed to resolve container type for " +
annotationType.getName());
}
return containerType;
}
Validate that the supplied containerType
is a proper container annotation for the supplied repeatable annotationType
(i.e. that it declares a value
attribute that holds an array of the annotationType
). Throws: - AnnotationConfigurationException – if the supplied
containerType
is not a valid container annotation for the supplied annotationType
Since: 4.3
/**
* Validate that the supplied {@code containerType} is a proper container
* annotation for the supplied repeatable {@code annotationType} (i.e.
* that it declares a {@code value} attribute that holds an array of the
* {@code annotationType}).
* @throws AnnotationConfigurationException if the supplied {@code containerType}
* is not a valid container annotation for the supplied {@code annotationType}
* @since 4.3
*/
private static void validateContainerType(Class<? extends Annotation> annotationType,
Class<? extends Annotation> containerType) {
try {
Method method = containerType.getDeclaredMethod(AnnotationUtils.VALUE);
Class<?> returnType = method.getReturnType();
if (!returnType.isArray() || returnType.getComponentType() != annotationType) {
String msg = String.format(
"Container type [%s] must declare a 'value' attribute for an array of type [%s]",
containerType.getName(), annotationType.getName());
throw new AnnotationConfigurationException(msg);
}
}
catch (Throwable ex) {
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
String msg = String.format("Invalid declaration of container type [%s] for repeatable annotation [%s]",
containerType.getName(), annotationType.getName());
throw new AnnotationConfigurationException(msg, ex);
}
}
Post-process the aggregated results into a set of synthesized annotations.
Params: - element – the annotated element
- aggregatedResults – the aggregated results for the given element
Returns: the set of annotations
/**
* Post-process the aggregated results into a set of synthesized annotations.
* @param element the annotated element
* @param aggregatedResults the aggregated results for the given element
* @return the set of annotations
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> Set<A> postProcessAndSynthesizeAggregatedResults(
AnnotatedElement element, List<AnnotationAttributes> aggregatedResults) {
Set<A> annotations = new LinkedHashSet<>();
for (AnnotationAttributes attributes : aggregatedResults) {
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
Class<? extends Annotation> annType = attributes.annotationType();
if (annType != null) {
annotations.add((A) AnnotationUtils.synthesizeAnnotation(attributes, annType, element));
}
}
return annotations;
}
Callback interface that is used to process annotations during a search.
Depending on the use case, a processor may choose to process a single target annotation, multiple target annotations, or all annotations discovered by the currently executing search. The term "target" in this context refers to a matching annotation (i.e. a specific annotation type that was found during the search).
Returning a non-null value from the process
method instructs the search algorithm to stop searching further; whereas, returning null
from the process
method instructs the search algorithm to continue searching for additional annotations. One exception to this rule applies to processors that aggregate results. If an aggregating processor returns a non-null value, that value will be added to the aggregated results and the search algorithm will continue.
Processors can optionally post-process the result of the process
method as the search algorithm goes back down the annotation hierarchy from an invocation of process
that returned a non-null value down to the AnnotatedElement
that was supplied as the starting point to the search algorithm.
Type parameters: - <T> – the type of result returned by the processor
/**
* Callback interface that is used to process annotations during a search.
* <p>Depending on the use case, a processor may choose to {@linkplain #process}
* a single target annotation, multiple target annotations, or all annotations
* discovered by the currently executing search. The term "target" in this
* context refers to a matching annotation (i.e. a specific annotation type
* that was found during the search).
* <p>Returning a non-null value from the {@link #process} method instructs
* the search algorithm to stop searching further; whereas, returning
* {@code null} from the {@link #process} method instructs the search
* algorithm to continue searching for additional annotations. One exception
* to this rule applies to processors that {@linkplain #aggregates aggregate}
* results. If an aggregating processor returns a non-null value, that value
* will be added to the {@linkplain #getAggregatedResults aggregated results}
* and the search algorithm will continue.
* <p>Processors can optionally {@linkplain #postProcess post-process} the
* result of the {@link #process} method as the search algorithm goes back
* down the annotation hierarchy from an invocation of {@link #process} that
* returned a non-null value down to the {@link AnnotatedElement} that was
* supplied as the starting point to the search algorithm.
* @param <T> the type of result returned by the processor
*/
private interface Processor<T> {
Process the supplied annotation.
The supplied annotation will be an actual target annotation that has been found by the search algorithm, unless this processor is configured to always process annotations in which case it may be some other annotation within an annotation hierarchy. In the latter case, the metaDepth
will have a value greater than 0
. In any case, it is up to concrete implementations of this method to decide what to do with the supplied annotation.
The metaDepth
parameter represents the depth of the annotation relative to the first annotated element in the annotation hierarchy. For example, an annotation that is present on a non-annotation element will have a depth
of 0; a meta-annotation will have a depth of 1; and a
meta-meta-annotation will have a depth of 2; etc.
Params: - annotatedElement – the element that is annotated with the supplied annotation, used for contextual logging; may be
null
if unknown - annotation – the annotation to process
- metaDepth – the meta-depth of the annotation
Returns: the result of the processing, or null
to continue searching for additional annotations
/**
* Process the supplied annotation.
* <p>The supplied annotation will be an actual target annotation
* that has been found by the search algorithm, unless this processor
* is configured to {@linkplain #alwaysProcesses always process}
* annotations in which case it may be some other annotation within an
* annotation hierarchy. In the latter case, the {@code metaDepth}
* will have a value greater than {@code 0}. In any case, it is
* up to concrete implementations of this method to decide what to
* do with the supplied annotation.
* <p>The {@code metaDepth} parameter represents the depth of the
* annotation relative to the first annotated element in the
* annotation hierarchy. For example, an annotation that is
* <em>present</em> on a non-annotation element will have a depth
* of 0; a meta-annotation will have a depth of 1; and a
* meta-meta-annotation will have a depth of 2; etc.
* @param annotatedElement the element that is annotated with the
* supplied annotation, used for contextual logging; may be
* {@code null} if unknown
* @param annotation the annotation to process
* @param metaDepth the meta-depth of the annotation
* @return the result of the processing, or {@code null} to continue
* searching for additional annotations
*/
@Nullable
T process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth);
Post-process the result returned by the process
method. The annotation
supplied to this method is an annotation that is present in the annotation hierarchy, between the initial AnnotatedElement
and an invocation of process
that returned a non-null value.
Params: - annotatedElement – the element that is annotated with the supplied annotation, used for contextual logging; may be
null
if unknown - annotation – the annotation to post-process
- result – the result to post-process
/**
* Post-process the result returned by the {@link #process} method.
* <p>The {@code annotation} supplied to this method is an annotation
* that is present in the annotation hierarchy, between the initial
* {@link AnnotatedElement} and an invocation of {@link #process}
* that returned a non-null value.
* @param annotatedElement the element that is annotated with the
* supplied annotation, used for contextual logging; may be
* {@code null} if unknown
* @param annotation the annotation to post-process
* @param result the result to post-process
*/
void postProcess(@Nullable AnnotatedElement annotatedElement, Annotation annotation, T result);
Determine if this processor always processes annotations regardless of
whether or not the target annotation has been found.
Returns: true
if this processor always processes annotationsSince: 4.3
/**
* Determine if this processor always processes annotations regardless of
* whether or not the target annotation has been found.
* @return {@code true} if this processor always processes annotations
* @since 4.3
*/
boolean alwaysProcesses();
Determine if this processor aggregates the results returned by process
. If this method returns true
, then getAggregatedResults()
must return a non-null value.
See Also: Returns: true
if this processor supports aggregated resultsSince: 4.3
/**
* Determine if this processor aggregates the results returned by {@link #process}.
* <p>If this method returns {@code true}, then {@link #getAggregatedResults()}
* must return a non-null value.
* @return {@code true} if this processor supports aggregated results
* @since 4.3
* @see #getAggregatedResults
*/
boolean aggregates();
Get the list of results aggregated by this processor.
NOTE: the processor does not aggregate the results itself. Rather, the search algorithm that uses this processor is responsible for asking this processor if it aggregates
results and then adding the post-processed results to the list returned by this method.
See Also: Returns: the list of results aggregated by this processor (never null
) Since: 4.3
/**
* Get the list of results aggregated by this processor.
* <p>NOTE: the processor does <strong>not</strong> aggregate the results
* itself. Rather, the search algorithm that uses this processor is
* responsible for asking this processor if it {@link #aggregates} results
* and then adding the post-processed results to the list returned by this
* method.
* @return the list of results aggregated by this processor (never {@code null})
* @since 4.3
* @see #aggregates
*/
List<T> getAggregatedResults();
}
Since: 4.2
/**
* {@link Processor} that {@linkplain #process(AnnotatedElement, Annotation, int)
* processes} annotations but does not {@linkplain #postProcess post-process} or
* {@linkplain #aggregates aggregate} results.
* @since 4.2
*/
private abstract static class SimpleAnnotationProcessor<T> implements Processor<T> {
private final boolean alwaysProcesses;
public SimpleAnnotationProcessor() {
this(false);
}
public SimpleAnnotationProcessor(boolean alwaysProcesses) {
this.alwaysProcesses = alwaysProcesses;
}
@Override
public final boolean alwaysProcesses() {
return this.alwaysProcesses;
}
@Override
public final void postProcess(@Nullable AnnotatedElement annotatedElement, Annotation annotation, T result) {
// no-op
}
@Override
public final boolean aggregates() {
return false;
}
@Override
public final List<T> getAggregatedResults() {
throw new UnsupportedOperationException("SimpleAnnotationProcessor does not support aggregated results");
}
}
Since: 4.3
/**
* {@link SimpleAnnotationProcessor} that always returns {@link Boolean#TRUE} when
* asked to {@linkplain #process(AnnotatedElement, Annotation, int) process} an
* annotation.
* @since 4.3
*/
static class AlwaysTrueBooleanAnnotationProcessor extends SimpleAnnotationProcessor<Boolean> {
@Override
public final Boolean process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
return Boolean.TRUE;
}
}
Processor
that gets the AnnotationAttributes
for the target annotation during the process
phase and then merges annotation attributes from lower levels in the annotation hierarchy during the postProcess
phase. A MergedAnnotationAttributesProcessor
may optionally be configured to aggregate results.
See Also: Since: 4.2
/**
* {@link Processor} that gets the {@code AnnotationAttributes} for the
* target annotation during the {@link #process} phase and then merges
* annotation attributes from lower levels in the annotation hierarchy
* during the {@link #postProcess} phase.
* <p>A {@code MergedAnnotationAttributesProcessor} may optionally be
* configured to {@linkplain #aggregates aggregate} results.
* @since 4.2
* @see AnnotationUtils#retrieveAnnotationAttributes
* @see AnnotationUtils#postProcessAnnotationAttributes
*/
private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> {
private final boolean classValuesAsString;
private final boolean nestedAnnotationsAsMap;
private final boolean aggregates;
private final List<AnnotationAttributes> aggregatedResults;
MergedAnnotationAttributesProcessor() {
this(false, false, false);
}
MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
this(classValuesAsString, nestedAnnotationsAsMap, false);
}
MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap,
boolean aggregates) {
this.classValuesAsString = classValuesAsString;
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
this.aggregates = aggregates;
this.aggregatedResults = (aggregates ? new ArrayList<>() : Collections.emptyList());
}
@Override
public boolean alwaysProcesses() {
return false;
}
@Override
public boolean aggregates() {
return this.aggregates;
}
@Override
public List<AnnotationAttributes> getAggregatedResults() {
return this.aggregatedResults;
}
@Override
@Nullable
public AnnotationAttributes process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,
this.classValuesAsString, this.nestedAnnotationsAsMap);
}
@Override
public void postProcess(@Nullable AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) {
annotation = AnnotationUtils.synthesizeAnnotation(annotation, element);
Class<? extends Annotation> targetAnnotationType = attributes.annotationType();
// Track which attribute values have already been replaced so that we can short
// circuit the search algorithms.
Set<String> valuesAlreadyReplaced = new HashSet<>();
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
String attributeName = attributeMethod.getName();
String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);
// Explicit annotation attribute override declared via @AliasFor
if (attributeOverrideName != null) {
if (valuesAlreadyReplaced.contains(attributeOverrideName)) {
continue;
}
List<String> targetAttributeNames = new ArrayList<>();
targetAttributeNames.add(attributeOverrideName);
valuesAlreadyReplaced.add(attributeOverrideName);
// Ensure all aliased attributes in the target annotation are overridden. (SPR-14069)
List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName);
if (aliases != null) {
for (String alias : aliases) {
if (!valuesAlreadyReplaced.contains(alias)) {
targetAttributeNames.add(alias);
valuesAlreadyReplaced.add(alias);
}
}
}
overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames);
}
// Implicit annotation attribute override based on convention
else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
overrideAttribute(element, annotation, attributes, attributeName, attributeName);
}
}
}
private void overrideAttributes(@Nullable AnnotatedElement element, Annotation annotation,
AnnotationAttributes attributes, String sourceAttributeName, List<String> targetAttributeNames) {
Object adaptedValue = getAdaptedValue(element, annotation, sourceAttributeName);
for (String targetAttributeName : targetAttributeNames) {
attributes.put(targetAttributeName, adaptedValue);
}
}
private void overrideAttribute(@Nullable AnnotatedElement element, Annotation annotation,
AnnotationAttributes attributes, String sourceAttributeName, String targetAttributeName) {
attributes.put(targetAttributeName, getAdaptedValue(element, annotation, sourceAttributeName));
}
@Nullable
private Object getAdaptedValue(
@Nullable AnnotatedElement element, Annotation annotation, String sourceAttributeName) {
Object value = AnnotationUtils.getValue(annotation, sourceAttributeName);
return AnnotationUtils.adaptValue(element, value, this.classValuesAsString, this.nestedAnnotationsAsMap);
}
}
}