Restore retrieval of plain annotations through direct presence checks
Includes deprecation of several AnnotationUtils methods and nullability refinements for passed-in function arguments at the MergedAnnotation API level... also, MergedAnnotation.getType() returns a Class now. Closes gh-22663 Closes gh-22685
This commit is contained in:
parent
f7a4850675
commit
210b178922
|
@ -521,9 +521,10 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
|||
* @param ann the Autowired annotation
|
||||
* @return whether the annotation indicates that a dependency is required
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected boolean determineRequiredStatus(MergedAnnotation<?> ann) {
|
||||
return determineRequiredStatus(
|
||||
ann.asMap(mergedAnnotation -> new AnnotationAttributes()));
|
||||
return determineRequiredStatus((AnnotationAttributes)
|
||||
ann.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -533,7 +534,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
|||
* or method when no beans are found.
|
||||
* @param ann the Autowired annotation
|
||||
* @return whether the annotation indicates that a dependency is required
|
||||
* @deprecated since 5.2 in favor of {@link #determineRequiredStatus(MergedAnnotation)}
|
||||
* @deprecated since 5.2, in favor of {@link #determineRequiredStatus(MergedAnnotation)}
|
||||
*/
|
||||
@Deprecated
|
||||
protected boolean determineRequiredStatus(AnnotationAttributes ann) {
|
||||
|
|
|
@ -18,7 +18,6 @@ package org.springframework.core.annotation;
|
|||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -30,6 +29,7 @@ import org.springframework.util.Assert;
|
|||
* Abstract base class for {@link MergedAnnotation} implementations.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.2
|
||||
* @param <A> the annotation type
|
||||
*/
|
||||
|
@ -167,19 +167,10 @@ abstract class AbstractMergedAnnotation<A extends Annotation> implements MergedA
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> asMap(MapValues... options) {
|
||||
return asMap(null, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<A> synthesize(
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> condition)
|
||||
public Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition)
|
||||
throws NoSuchElementException {
|
||||
|
||||
if (condition == null || condition.test(this)) {
|
||||
return Optional.of(synthesize());
|
||||
}
|
||||
return Optional.empty();
|
||||
return (condition.test(this) ? Optional.of(synthesize()) : Optional.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -199,7 +190,7 @@ abstract class AbstractMergedAnnotation<A extends Annotation> implements MergedA
|
|||
T value = getAttributeValue(attributeName, type);
|
||||
if (value == null) {
|
||||
throw new NoSuchElementException("No attribute named '" + attributeName +
|
||||
"' present in merged annotation " + getType());
|
||||
"' present in merged annotation " + getType().getName());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.lang.reflect.AnnotatedElement;
|
|||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -137,9 +136,7 @@ public abstract class AnnotatedElementUtils {
|
|||
* @see #getMetaAnnotationTypes(AnnotatedElement, Class)
|
||||
* @see #hasMetaAnnotationTypes
|
||||
*/
|
||||
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
|
||||
String annotationName) {
|
||||
|
||||
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
|
||||
for (Annotation annotation : element.getAnnotations()) {
|
||||
if (annotation.annotationType().getName().equals(annotationName)) {
|
||||
return getMetaAnnotationTypes(element, annotation);
|
||||
|
@ -148,14 +145,12 @@ public abstract class AnnotatedElementUtils {
|
|||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
private static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
|
||||
@Nullable Annotation annotation) {
|
||||
|
||||
private static Set<String> getMetaAnnotationTypes(AnnotatedElement element, @Nullable Annotation annotation) {
|
||||
if (annotation == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return getAnnotations(annotation.annotationType()).stream()
|
||||
.map(MergedAnnotation::getType)
|
||||
.map(mergedAnnotation -> mergedAnnotation.getType().getName())
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
}
|
||||
|
||||
|
@ -171,11 +166,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @since 4.2.3
|
||||
* @see #getMetaAnnotationTypes
|
||||
*/
|
||||
public static boolean hasMetaAnnotationTypes(AnnotatedElement element,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
return getAnnotations(element).stream(annotationType)
|
||||
.anyMatch(MergedAnnotation::isMetaPresent);
|
||||
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||
return getAnnotations(element).stream(annotationType).anyMatch(MergedAnnotation::isMetaPresent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,11 +182,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @return {@code true} if a matching meta-annotation is present
|
||||
* @see #getMetaAnnotationTypes
|
||||
*/
|
||||
public static boolean hasMetaAnnotationTypes(AnnotatedElement element,
|
||||
String annotationName) {
|
||||
|
||||
return getAnnotations(element).stream(annotationName)
|
||||
.anyMatch(MergedAnnotation::isMetaPresent);
|
||||
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
|
||||
return getAnnotations(element).stream(annotationName).anyMatch(MergedAnnotation::isMetaPresent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,9 +200,16 @@ public abstract class AnnotatedElementUtils {
|
|||
* @since 4.2.3
|
||||
* @see #hasAnnotation(AnnotatedElement, Class)
|
||||
*/
|
||||
public static boolean isAnnotated(AnnotatedElement element,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
public static boolean isAnnotated(AnnotatedElement element,Class<? extends Annotation> annotationType) {
|
||||
// Shortcut: directly present on the element, with no processing needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
return element.isAnnotationPresent(annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return false;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return getAnnotations(element).isPresent(annotationType);
|
||||
}
|
||||
|
||||
|
@ -278,8 +274,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @see #getAllAnnotationAttributes(AnnotatedElement, String)
|
||||
*/
|
||||
@Nullable
|
||||
public static AnnotationAttributes getMergedAnnotationAttributes(
|
||||
AnnotatedElement element, String annotationName) {
|
||||
public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element,
|
||||
String annotationName) {
|
||||
|
||||
return getMergedAnnotationAttributes(element, annotationName, false, false);
|
||||
}
|
||||
|
@ -311,9 +307,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
|
||||
*/
|
||||
@Nullable
|
||||
public static AnnotationAttributes getMergedAnnotationAttributes(
|
||||
AnnotatedElement element, String annotationName, boolean classValuesAsString,
|
||||
boolean nestedAnnotationsAsMap) {
|
||||
public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element,
|
||||
String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
MergedAnnotation<?> mergedAnnotation = getAnnotations(element)
|
||||
.get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
|
@ -328,30 +323,23 @@ public abstract class AnnotatedElementUtils {
|
|||
* 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) {
|
||||
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return null;
|
||||
}
|
||||
A annotation = AnnotationsScanner.getDeclaredAnnotation(element, annotationType);
|
||||
if (annotation != null) {
|
||||
return AnnotationUtils.synthesizeAnnotation(annotation, element);
|
||||
}
|
||||
public static <A extends Annotation> A getMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
|
||||
// Shortcut: directly present on the element, with no merging needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
return element.getDeclaredAnnotation(annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return null;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return getAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
|
@ -515,9 +503,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* 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) {
|
||||
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
|
||||
String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
|
||||
|
||||
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap);
|
||||
return getAnnotations(element).stream(annotationName)
|
||||
|
@ -540,16 +527,16 @@ public abstract class AnnotatedElementUtils {
|
|||
* @since 4.3
|
||||
* @see #isAnnotated(AnnotatedElement, Class)
|
||||
*/
|
||||
public static boolean hasAnnotation(AnnotatedElement element,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||
// Shortcut: directly present on the element, with no processing needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
return element.isAnnotationPresent(annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return false;
|
||||
}
|
||||
if (AnnotationsScanner.getDeclaredAnnotation(element, annotationType) != null) {
|
||||
return true;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return findAnnotations(element).isPresent(annotationType);
|
||||
}
|
||||
|
||||
|
@ -581,9 +568,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
|
||||
*/
|
||||
@Nullable
|
||||
public static AnnotationAttributes findMergedAnnotationAttributes(
|
||||
AnnotatedElement element, Class<? extends Annotation> annotationType,
|
||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
|
||||
Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
MergedAnnotation<?> mergedAnnotation = findAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
|
@ -618,9 +604,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
|
||||
*/
|
||||
@Nullable
|
||||
public static AnnotationAttributes findMergedAnnotationAttributes(
|
||||
AnnotatedElement element, String annotationName, boolean classValuesAsString,
|
||||
boolean nestedAnnotationsAsMap) {
|
||||
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
|
||||
String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
MergedAnnotation<?> mergedAnnotation = findAnnotations(element)
|
||||
.get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
|
@ -646,16 +631,16 @@ public abstract class AnnotatedElementUtils {
|
|||
* @see #getMergedAnnotationAttributes(AnnotatedElement, Class)
|
||||
*/
|
||||
@Nullable
|
||||
public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element,
|
||||
Class<A> annotationType) {
|
||||
|
||||
A annotation = AnnotationsScanner.getDeclaredAnnotation(element, annotationType);
|
||||
if (annotation != null) {
|
||||
return AnnotationUtils.synthesizeAnnotation(annotation, element);
|
||||
public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
|
||||
// Shortcut: directly present on the element, with no merging needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
return element.getDeclaredAnnotation(annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return null;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return findAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
|
@ -680,9 +665,7 @@ public abstract class AnnotatedElementUtils {
|
|||
* @see #findMergedAnnotation(AnnotatedElement, Class)
|
||||
* @see #getAllMergedAnnotations(AnnotatedElement, Class)
|
||||
*/
|
||||
public static <A extends Annotation> Set<A> findAllMergedAnnotations(
|
||||
AnnotatedElement element, Class<A> annotationType) {
|
||||
|
||||
public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
|
||||
return findAnnotations(element).stream(annotationType)
|
||||
.sorted(highAggregateIndexesFirst())
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
|
@ -706,9 +689,7 @@ public abstract class AnnotatedElementUtils {
|
|||
* @since 5.1
|
||||
* @see #findAllMergedAnnotations(AnnotatedElement, Class)
|
||||
*/
|
||||
public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element,
|
||||
Set<Class<? extends Annotation>> annotationTypes) {
|
||||
|
||||
public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) {
|
||||
return findAnnotations(element).stream()
|
||||
.filter(MergedAnnotationPredicates.typeIn(annotationTypes))
|
||||
.sorted(highAggregateIndexesFirst())
|
||||
|
@ -739,8 +720,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @see #findAllMergedAnnotations(AnnotatedElement, Class)
|
||||
* @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
|
||||
*/
|
||||
public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(
|
||||
AnnotatedElement element, Class<A> annotationType) {
|
||||
public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element,
|
||||
Class<A> annotationType) {
|
||||
|
||||
return findMergedRepeatableAnnotations(element, annotationType, null);
|
||||
}
|
||||
|
@ -771,9 +752,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @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) {
|
||||
public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element,
|
||||
Class<A> annotationType, @Nullable Class<? extends Annotation> containerType) {
|
||||
|
||||
return findRepeatableAnnotations(element, containerType, annotationType)
|
||||
.stream(annotationType)
|
||||
|
@ -783,43 +763,40 @@ public abstract class AnnotatedElementUtils {
|
|||
|
||||
private static MergedAnnotations getAnnotations(AnnotatedElement element) {
|
||||
return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationUtils.JAVA_LANG_ANNOTATION_FILTER);
|
||||
RepeatableContainers.none(), AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
private static MergedAnnotations getRepeatableAnnotations(AnnotatedElement element,
|
||||
@Nullable Class<? extends Annotation> containerType,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
@Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
|
||||
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
|
||||
return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
repeatableContainers, AnnotationUtils.JAVA_LANG_ANNOTATION_FILTER);
|
||||
repeatableContainers, AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
private static MergedAnnotations findAnnotations(AnnotatedElement element) {
|
||||
return MergedAnnotations.from(element, SearchStrategy.EXHAUSTIVE,
|
||||
RepeatableContainers.none(), AnnotationUtils.JAVA_LANG_ANNOTATION_FILTER);
|
||||
RepeatableContainers.none(), AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
private static MergedAnnotations findRepeatableAnnotations(AnnotatedElement element,
|
||||
@Nullable Class<? extends Annotation> containerType,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
@Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
|
||||
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
|
||||
return MergedAnnotations.from(element, SearchStrategy.EXHAUSTIVE,
|
||||
repeatableContainers, AnnotationUtils.JAVA_LANG_ANNOTATION_FILTER);
|
||||
repeatableContainers, AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
private static Object parentAndType(MergedAnnotation<Annotation> annotation) {
|
||||
if (annotation.getParent() == null) {
|
||||
return annotation.getType();
|
||||
return annotation.getType().getName();
|
||||
}
|
||||
return annotation.getParent().getType() + ":" + annotation.getParent().getType();
|
||||
return annotation.getParent().getType().getName() + ":" + annotation.getParent().getType().getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MultiValueMap<String, Object> nullIfEmpty(
|
||||
MultiValueMap<String, Object> map) {
|
||||
return map.isEmpty() ? null : map;
|
||||
private static MultiValueMap<String, Object> nullIfEmpty(MultiValueMap<String, Object> map) {
|
||||
return (map.isEmpty() ? null : map);
|
||||
}
|
||||
|
||||
private static <A extends Annotation> Comparator<MergedAnnotation<A>> highAggregateIndexesFirst() {
|
||||
|
@ -834,7 +811,7 @@ public abstract class AnnotatedElementUtils {
|
|||
if (!annotation.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
return annotation.asMap(mergedAnnotation -> new AnnotationAttributes(),
|
||||
return annotation.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType()),
|
||||
MapValues.of(classValuesAsString, nestedAnnotationsAsMap));
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to filter specific annotation types.
|
||||
|
@ -96,52 +92,4 @@ public interface AnnotationFilter {
|
|||
return new PackagesAnnotationFilter(packages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link AnnotationFilter} that is the most appropriate for, and
|
||||
* will always match the given annotation type. Whenever possible,
|
||||
* {@link AnnotationFilter#PLAIN} will be returned.
|
||||
* @param annotationType the annotation type to check
|
||||
* @return the most appropriate annotation filter
|
||||
*/
|
||||
static AnnotationFilter mostAppropriateFor(Class<?> annotationType) {
|
||||
return (PLAIN.matches(annotationType) ? NONE : PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link AnnotationFilter} that is the most appropriate for, and
|
||||
* will always match all the given annotation types. Whenever possible,
|
||||
* {@link AnnotationFilter#PLAIN} will be returned.
|
||||
* @param annotationTypes the annotation types to check
|
||||
* @return the most appropriate annotation filter
|
||||
*/
|
||||
static AnnotationFilter mostAppropriateFor(Class<?>... annotationTypes) {
|
||||
return mostAppropriateFor(Arrays.asList(annotationTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link AnnotationFilter} that is the most appropriate for, and
|
||||
* will always match all the given annotation types. Whenever possible,
|
||||
* {@link AnnotationFilter#PLAIN} will be returned.
|
||||
* @param annotationTypes the annotation types to check (may be class names
|
||||
* or class types)
|
||||
* @return the most appropriate annotation filter
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static AnnotationFilter mostAppropriateFor(Collection<?> annotationTypes) {
|
||||
for (Object annotationType : annotationTypes) {
|
||||
if (annotationType == null) {
|
||||
continue;
|
||||
}
|
||||
Assert.isTrue(annotationType instanceof Class || annotationType instanceof String,
|
||||
"AnnotationType must be a Class or String");
|
||||
if (annotationType instanceof Class && PLAIN.matches((Class<Annotation>) annotationType)) {
|
||||
return NONE;
|
||||
}
|
||||
if (annotationType instanceof String && PLAIN.matches((String) annotationType)) {
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
return PLAIN;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,8 +55,7 @@ final class AnnotationTypeMappings {
|
|||
private final List<AnnotationTypeMapping> mappings;
|
||||
|
||||
|
||||
private AnnotationTypeMappings(AnnotationFilter filter,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
private AnnotationTypeMappings(AnnotationFilter filter, Class<? extends Annotation> annotationType) {
|
||||
this.filter = filter;
|
||||
this.mappings = new ArrayList<>();
|
||||
addAllMappings(annotationType);
|
||||
|
@ -97,27 +96,23 @@ final class AnnotationTypeMappings {
|
|||
}
|
||||
}
|
||||
|
||||
private void addIfPossible(Deque<AnnotationTypeMapping> queue,
|
||||
AnnotationTypeMapping parent, Annotation annotation) {
|
||||
addIfPossible(queue, parent, annotation.annotationType(), annotation);
|
||||
private void addIfPossible(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping parent, Annotation ann) {
|
||||
addIfPossible(queue, parent, ann.annotationType(), ann);
|
||||
}
|
||||
|
||||
private void addIfPossible(Deque<AnnotationTypeMapping> queue,
|
||||
@Nullable AnnotationTypeMapping parent,
|
||||
Class<? extends Annotation> annotationType, @Nullable Annotation annotation) {
|
||||
private void addIfPossible(Deque<AnnotationTypeMapping> queue, @Nullable AnnotationTypeMapping parent,
|
||||
Class<? extends Annotation> annotationType, @Nullable Annotation ann) {
|
||||
|
||||
try {
|
||||
queue.addLast(new AnnotationTypeMapping(parent, annotationType, annotation));
|
||||
queue.addLast(new AnnotationTypeMapping(parent, annotationType, ann));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (ex instanceof AnnotationConfigurationException) {
|
||||
throw (AnnotationConfigurationException) ex;
|
||||
}
|
||||
if (failureLogger.isEnabled()) {
|
||||
failureLogger.log(
|
||||
"Failed to introspect meta-annotation "
|
||||
+ annotationType.getName(),
|
||||
(parent != null) ? parent.getAnnotationType() : null, ex);
|
||||
failureLogger.log("Failed to introspect meta-annotation " + annotationType.getName(),
|
||||
(parent != null ? parent.getAnnotationType() : null), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +162,7 @@ final class AnnotationTypeMappings {
|
|||
* @return type mappings for the annotation type
|
||||
*/
|
||||
static AnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType) {
|
||||
return forAnnotationType(annotationType, AnnotationFilter.mostAppropriateFor(annotationType));
|
||||
return forAnnotationType(annotationType, AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,7 +192,6 @@ final class AnnotationTypeMappings {
|
|||
|
||||
private final Map<Class<? extends Annotation>, AnnotationTypeMappings> mappings;
|
||||
|
||||
|
||||
/**
|
||||
* Create a cache instance with the specified filter.
|
||||
* @param filter the annotation filter
|
||||
|
@ -207,7 +201,6 @@ final class AnnotationTypeMappings {
|
|||
this.mappings = new ConcurrentReferenceHashMap<>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return or create {@link AnnotationTypeMappings} for the specified
|
||||
* annotation type.
|
||||
|
@ -218,8 +211,7 @@ final class AnnotationTypeMappings {
|
|||
return this.mappings.computeIfAbsent(annotationType, this::createMappings);
|
||||
}
|
||||
|
||||
AnnotationTypeMappings createMappings(
|
||||
Class<? extends Annotation> annotationType) {
|
||||
AnnotationTypeMappings createMappings(Class<? extends Annotation> annotationType) {
|
||||
return new AnnotationTypeMappings(this.filter, annotationType);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.Mirr
|
|||
import org.springframework.core.annotation.MergedAnnotation.MapValues;
|
||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -51,12 +50,12 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* <p>As a general rule for runtime-retained annotations (e.g. for transaction
|
||||
* control, authorization, or service exposure), always use the lookup methods
|
||||
* on this class (e.g., {@link #findAnnotation(Method, Class)},
|
||||
* {@link #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)})
|
||||
* instead of the plain annotation lookup methods in the JDK. You can still
|
||||
* explicitly choose between a <em>get</em> lookup on the given class level only
|
||||
* ({@link #getAnnotation(Method, Class)}) and a <em>find</em> lookup in the entire
|
||||
* inheritance hierarchy of the given method ({@link #findAnnotation(Method, Class)}).
|
||||
* on this class (e.g., {@link #findAnnotation(Method, Class)} and
|
||||
* {@link #getAnnotation(Method, Class)}) instead of the plain annotation lookup
|
||||
* methods in the JDK. You can still explicitly choose between a <em>get</em>
|
||||
* lookup on the given class level only ({@link #getAnnotation(Method, Class)})
|
||||
* and a <em>find</em> lookup in the entire inheritance hierarchy of the given
|
||||
* method ({@link #findAnnotation(Method, Class)}).
|
||||
*
|
||||
* <h3>Terminology</h3>
|
||||
* The terms <em>directly present</em>, <em>indirectly present</em>, and
|
||||
|
@ -111,14 +110,13 @@ public abstract class AnnotationUtils {
|
|||
*/
|
||||
public static final String VALUE = MergedAnnotation.VALUE;
|
||||
|
||||
|
||||
static final AnnotationFilter JAVA_LANG_ANNOTATION_FILTER =
|
||||
private static final AnnotationFilter JAVA_LANG_ANNOTATION_FILTER =
|
||||
AnnotationFilter.packages("java.lang.annotation");
|
||||
|
||||
|
||||
private static Map<Class<? extends Annotation>, Map<String, DefaultValueHolder>> defaultValuesCache =
|
||||
new ConcurrentReferenceHashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether the given class is a candidate for carrying one of the specified
|
||||
* annotations (at type, method or field level).
|
||||
|
@ -180,10 +178,18 @@ public abstract class AnnotationUtils {
|
|||
* @return the first matching annotation, or {@code null} if not found
|
||||
* @since 4.0
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public static <A extends Annotation> A getAnnotation(Annotation annotation,
|
||||
Class<A> annotationType) {
|
||||
|
||||
public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) {
|
||||
// Shortcut: directly present on the element, with no merging needed?
|
||||
if (annotationType.isInstance(annotation)) {
|
||||
return synthesizeAnnotation((A) annotation, annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotation)) {
|
||||
return null;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return MergedAnnotations.from(annotation)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);
|
||||
|
@ -202,15 +208,27 @@ public abstract class AnnotationUtils {
|
|||
* @since 3.1
|
||||
*/
|
||||
@Nullable
|
||||
public static <A extends Annotation> A getAnnotation(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
|
||||
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
// Shortcut: directly present on the element, with no merging needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
return annotatedElement.getAnnotation(annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) {
|
||||
return null;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.PLAIN)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);
|
||||
}
|
||||
|
||||
private static <A extends Annotation> boolean isSingleLevelPresent(MergedAnnotation<A> mergedAnnotation) {
|
||||
int depth = mergedAnnotation.getDepth();
|
||||
return (depth == 0 || depth == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single {@link Annotation} of {@code annotationType} from the
|
||||
* supplied {@link Method}, where the annotation is either <em>present</em>
|
||||
|
@ -226,13 +244,9 @@ public abstract class AnnotationUtils {
|
|||
* @see #getAnnotation(AnnotatedElement, Class)
|
||||
*/
|
||||
@Nullable
|
||||
public static <A extends Annotation> A getAnnotation(Method method,
|
||||
Class<A> annotationType) {
|
||||
|
||||
return MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.PLAIN)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);
|
||||
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
|
||||
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
return getAnnotation((AnnotatedElement) resolvedMethod, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -245,14 +259,18 @@ public abstract class AnnotationUtils {
|
|||
* failed to resolve at runtime)
|
||||
* @since 4.0.8
|
||||
* @see AnnotatedElement#getAnnotations()
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) {
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.NONE).stream()
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationArray());
|
||||
try {
|
||||
return synthesizeAnnotationArray(annotatedElement.getAnnotations(), annotatedElement);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleIntrospectionFailure(annotatedElement, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,14 +284,18 @@ public abstract class AnnotationUtils {
|
|||
* failed to resolve at runtime)
|
||||
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
|
||||
* @see AnnotatedElement#getAnnotations()
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public static Annotation[] getAnnotations(Method method) {
|
||||
return MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.NONE).stream()
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationArray());
|
||||
try {
|
||||
return synthesizeAnnotationArray(BridgeMethodResolver.findBridgedMethod(method).getAnnotations(), method);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleIntrospectionFailure(method, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -302,9 +324,11 @@ public abstract class AnnotationUtils {
|
|||
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
|
||||
* @see java.lang.annotation.Repeatable
|
||||
* @see java.lang.reflect.AnnotatedElement#getAnnotationsByType
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
public static <A extends Annotation> Set<A> getRepeatableAnnotations(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
@Deprecated
|
||||
public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement,
|
||||
Class<A> annotationType) {
|
||||
|
||||
return getRepeatableAnnotations(annotatedElement, annotationType, null);
|
||||
}
|
||||
|
@ -338,17 +362,17 @@ public abstract class AnnotationUtils {
|
|||
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
|
||||
* @see java.lang.annotation.Repeatable
|
||||
* @see java.lang.reflect.AnnotatedElement#getAnnotationsByType
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
public static <A extends Annotation> Set<A> getRepeatableAnnotations(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType,
|
||||
@Nullable Class<? extends Annotation> containerAnnotationType) {
|
||||
@Deprecated
|
||||
public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement,
|
||||
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
|
||||
|
||||
RepeatableContainers repeatableContainers = containerAnnotationType != null ?
|
||||
RepeatableContainers repeatableContainers = (containerAnnotationType != null ?
|
||||
RepeatableContainers.of(annotationType, containerAnnotationType) :
|
||||
RepeatableContainers.standardRepeatables();
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
RepeatableContainers.standardRepeatables());
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.SUPER_CLASS,
|
||||
repeatableContainers, annotationFilter)
|
||||
repeatableContainers, AnnotationFilter.PLAIN)
|
||||
.stream(annotationType)
|
||||
.filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex))
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
|
@ -382,9 +406,11 @@ public abstract class AnnotationUtils {
|
|||
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
|
||||
* @see java.lang.annotation.Repeatable
|
||||
* @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
@Deprecated
|
||||
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement,
|
||||
Class<A> annotationType) {
|
||||
|
||||
return getDeclaredRepeatableAnnotations(annotatedElement, annotationType, null);
|
||||
}
|
||||
|
@ -418,18 +444,17 @@ public abstract class AnnotationUtils {
|
|||
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
|
||||
* @see java.lang.annotation.Repeatable
|
||||
* @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType,
|
||||
@Nullable Class<? extends Annotation> containerAnnotationType) {
|
||||
@Deprecated
|
||||
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement,
|
||||
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
|
||||
|
||||
RepeatableContainers repeatableContainers = containerAnnotationType != null ?
|
||||
RepeatableContainers.of(annotationType, containerAnnotationType) :
|
||||
RepeatableContainers.standardRepeatables();
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(
|
||||
annotationType, containerAnnotationType);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT,
|
||||
repeatableContainers, annotationFilter).stream(annotationType)
|
||||
repeatableContainers, AnnotationFilter.PLAIN).stream(annotationType)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
@ -452,11 +477,21 @@ public abstract class AnnotationUtils {
|
|||
*/
|
||||
@Nullable
|
||||
public static <A extends Annotation> A findAnnotation(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
AnnotatedElement annotatedElement, @Nullable Class<A> annotationType) {
|
||||
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
if (annotationType == null) {
|
||||
return null;
|
||||
}
|
||||
// Shortcut: directly present on the element, with no merging needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
return annotatedElement.getDeclaredAnnotation(annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) {
|
||||
return null;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
|
@ -481,10 +516,16 @@ public abstract class AnnotationUtils {
|
|||
if (annotationType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
// Shortcut: directly present on the element, with no merging needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
return method.getDeclaredAnnotation(annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(method)) {
|
||||
return null;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
|
@ -512,10 +553,20 @@ public abstract class AnnotationUtils {
|
|||
* @return the first matching annotation, or {@code null} if not found
|
||||
*/
|
||||
@Nullable
|
||||
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(clazz, SearchStrategy.EXHAUSTIVE,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
public static <A extends Annotation> A findAnnotation(Class<?> clazz, @Nullable Class<A> annotationType) {
|
||||
if (annotationType == null) {
|
||||
return null;
|
||||
}
|
||||
// Shortcut: directly present on the element, with no merging needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
return clazz.getDeclaredAnnotation(annotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) {
|
||||
return null;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return MergedAnnotations.from(clazz, SearchStrategy.EXHAUSTIVE)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
|
@ -539,9 +590,9 @@ public abstract class AnnotationUtils {
|
|||
* or {@code null} if not found
|
||||
* @see Class#isAnnotationPresent(Class)
|
||||
* @see Class#getDeclaredAnnotations()
|
||||
* @see #findAnnotationDeclaringClassForTypes(List, Class)
|
||||
* @see #isAnnotationDeclaredLocally(Class, Class)
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public static Class<?> findAnnotationDeclaringClass(
|
||||
Class<? extends Annotation> annotationType, @Nullable Class<?> clazz) {
|
||||
|
@ -550,9 +601,7 @@ public abstract class AnnotationUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS)
|
||||
.get(annotationType, MergedAnnotation::isDirectlyPresent)
|
||||
.getSource();
|
||||
}
|
||||
|
@ -578,9 +627,9 @@ public abstract class AnnotationUtils {
|
|||
* @since 3.2.2
|
||||
* @see Class#isAnnotationPresent(Class)
|
||||
* @see Class#getDeclaredAnnotations()
|
||||
* @see #findAnnotationDeclaringClass(Class, Class)
|
||||
* @see #isAnnotationDeclaredLocally(Class, Class)
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public static Class<?> findAnnotationDeclaringClassForTypes(
|
||||
List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
|
||||
|
@ -589,9 +638,7 @@ public abstract class AnnotationUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationTypes);
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS)
|
||||
.stream()
|
||||
.filter(MergedAnnotationPredicates.typeIn(annotationTypes).and(MergedAnnotation::isDirectlyPresent))
|
||||
.map(MergedAnnotation::getSource)
|
||||
|
@ -605,16 +652,13 @@ public abstract class AnnotationUtils {
|
|||
* <p>The supplied {@link Class} may represent any type.
|
||||
* <p>Meta-annotations will <em>not</em> be searched.
|
||||
* <p>Note: This method does <strong>not</strong> determine if the annotation
|
||||
* is {@linkplain java.lang.annotation.Inherited inherited}. For greater
|
||||
* clarity regarding inherited annotations, consider using
|
||||
* {@link #isAnnotationInherited(Class, Class)} instead.
|
||||
* is {@linkplain java.lang.annotation.Inherited inherited}.
|
||||
* @param annotationType the annotation type to look for
|
||||
* @param clazz the class to check for the annotation on
|
||||
* @return {@code true} if an annotation of the specified {@code annotationType}
|
||||
* is <em>directly present</em>
|
||||
* @see java.lang.Class#getDeclaredAnnotations()
|
||||
* @see java.lang.Class#getDeclaredAnnotation(Class)
|
||||
* @see #isAnnotationInherited(Class, Class)
|
||||
*/
|
||||
public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
return MergedAnnotations.from(clazz).get(annotationType).isDirectlyPresent();
|
||||
|
@ -638,7 +682,9 @@ public abstract class AnnotationUtils {
|
|||
* is <em>present</em> and <em>inherited</em>
|
||||
* @see Class#isAnnotationPresent(Class)
|
||||
* @see #isAnnotationDeclaredLocally(Class, Class)
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
return MergedAnnotations.from(clazz, SearchStrategy.INHERITED_ANNOTATIONS)
|
||||
.stream(annotationType)
|
||||
|
@ -654,11 +700,27 @@ public abstract class AnnotationUtils {
|
|||
* @param metaAnnotationType the type of meta-annotation to search for
|
||||
* @return {@code true} if such an annotation is meta-present
|
||||
* @since 4.2.1
|
||||
* @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType,
|
||||
@Nullable Class<? extends Annotation> metaAnnotationType) {
|
||||
|
||||
return MergedAnnotations.from(annotationType, SearchStrategy.EXHAUSTIVE).isPresent(annotationType);
|
||||
if (metaAnnotationType == null) {
|
||||
return false;
|
||||
}
|
||||
// Shortcut: directly present on the element, with no merging needed?
|
||||
if (AnnotationFilter.PLAIN.matches(annotationType) ||
|
||||
AnnotationFilter.PLAIN.matches(metaAnnotationType)) {
|
||||
return annotationType.isAnnotationPresent(metaAnnotationType);
|
||||
}
|
||||
// Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotationType)) {
|
||||
return false;
|
||||
}
|
||||
// Exhaustive retrieval of merged annotations...
|
||||
return (MergedAnnotations.from(
|
||||
annotationType, SearchStrategy.INHERITED_ANNOTATIONS).isPresent(metaAnnotationType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -731,8 +793,8 @@ public abstract class AnnotationUtils {
|
|||
* corresponding attribute values as values (never {@code null})
|
||||
* @see #getAnnotationAttributes(Annotation, boolean, boolean)
|
||||
*/
|
||||
public static Map<String, Object> getAnnotationAttributes(Annotation annotation,
|
||||
boolean classValuesAsString) {
|
||||
public static Map<String, Object> getAnnotationAttributes(
|
||||
Annotation annotation, boolean classValuesAsString) {
|
||||
|
||||
return getAnnotationAttributes(annotation, classValuesAsString, false);
|
||||
}
|
||||
|
@ -753,11 +815,10 @@ public abstract class AnnotationUtils {
|
|||
* and corresponding attribute values as values (never {@code null})
|
||||
* @since 3.1.1
|
||||
*/
|
||||
public static AnnotationAttributes getAnnotationAttributes(Annotation annotation,
|
||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
public static AnnotationAttributes getAnnotationAttributes(
|
||||
Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
return getAnnotationAttributes(null, annotation, classValuesAsString,
|
||||
nestedAnnotationsAsMap);
|
||||
return getAnnotationAttributes(null, annotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -801,22 +862,14 @@ public abstract class AnnotationUtils {
|
|||
@Nullable AnnotatedElement annotatedElement, Annotation annotation,
|
||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
ClassLoader classLoader = annotation.annotationType().getClassLoader();
|
||||
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap);
|
||||
return MergedAnnotation.from(annotatedElement, annotation)
|
||||
.withNonMergedAttributes()
|
||||
.asMap(getAnnotationAttributesFactory(classLoader), mapValues);
|
||||
.asMap(getAnnotationAttributesFactory(), mapValues);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Function<MergedAnnotation<?>, AnnotationAttributes> getAnnotationAttributesFactory(
|
||||
ClassLoader classLoader) {
|
||||
|
||||
return annotation -> {
|
||||
Class<Annotation> type = (Class<Annotation>) ClassUtils
|
||||
.resolveClassName(annotation.getType(), classLoader);
|
||||
return new AnnotationAttributes(type, true);
|
||||
};
|
||||
private static Function<MergedAnnotation<?>, AnnotationAttributes> getAnnotationAttributesFactory() {
|
||||
return (annotation -> new AnnotationAttributes(annotation.getType(), true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -861,9 +914,8 @@ public abstract class AnnotationUtils {
|
|||
}
|
||||
else {
|
||||
// If we have nested annotations, we need them as nested maps
|
||||
AnnotationAttributes attributes = MergedAnnotation.from(annotationType).asMap(
|
||||
getAnnotationAttributesFactory(annotationType.getClassLoader()),
|
||||
MapValues.ANNOTATION_TO_MAP);
|
||||
AnnotationAttributes attributes = MergedAnnotation.from(annotationType)
|
||||
.asMap(getAnnotationAttributesFactory(), MapValues.ANNOTATION_TO_MAP);
|
||||
for (Map.Entry<String, Object> element : attributes.entrySet()) {
|
||||
result.put(element.getKey(), new DefaultValueHolder(element.getValue()));
|
||||
}
|
||||
|
@ -927,18 +979,14 @@ public abstract class AnnotationUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static Object getAttributeValueForMirrorResolution(Method attribute,
|
||||
Object attributes) {
|
||||
|
||||
private static Object getAttributeValueForMirrorResolution(Method attribute, Object attributes) {
|
||||
Object result = ((AnnotationAttributes) attributes).get(attribute.getName());
|
||||
return result instanceof DefaultValueHolder ?
|
||||
((DefaultValueHolder) result).defaultValue :
|
||||
result;
|
||||
return (result instanceof DefaultValueHolder ? ((DefaultValueHolder) result).defaultValue : result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Object adaptValue(@Nullable Object annotatedElement,
|
||||
@Nullable Object value, boolean classValuesAsString) {
|
||||
private static Object adaptValue(
|
||||
@Nullable Object annotatedElement, @Nullable Object value, boolean classValuesAsString) {
|
||||
|
||||
if (classValuesAsString) {
|
||||
if (value instanceof Class) {
|
||||
|
@ -962,8 +1010,7 @@ public abstract class AnnotationUtils {
|
|||
Annotation[] synthesized = (Annotation[]) Array.newInstance(
|
||||
annotations.getClass().getComponentType(), annotations.length);
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
synthesized[i] = MergedAnnotation.from(annotatedElement, annotations[i])
|
||||
.synthesize();
|
||||
synthesized[i] = MergedAnnotation.from(annotatedElement, annotations[i]).synthesize();
|
||||
}
|
||||
return synthesized;
|
||||
}
|
||||
|
@ -994,9 +1041,7 @@ public abstract class AnnotationUtils {
|
|||
* @see #getValue(Annotation)
|
||||
*/
|
||||
@Nullable
|
||||
public static Object getValue(@Nullable Annotation annotation,
|
||||
@Nullable String attributeName) {
|
||||
|
||||
public static Object getValue(@Nullable Annotation annotation, @Nullable String attributeName) {
|
||||
if (annotation == null || !StringUtils.hasText(attributeName)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1047,8 +1092,7 @@ public abstract class AnnotationUtils {
|
|||
* @param ex the exception that we encountered
|
||||
* @see #rethrowAnnotationConfigurationException
|
||||
*/
|
||||
private static void handleIntrospectionFailure(@Nullable AnnotatedElement element,
|
||||
Throwable ex) {
|
||||
private static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
|
||||
boolean meta = false;
|
||||
|
@ -1085,12 +1129,8 @@ public abstract class AnnotationUtils {
|
|||
* @see #getDefaultValue(Class, String)
|
||||
*/
|
||||
@Nullable
|
||||
public static Object getDefaultValue(@Nullable Annotation annotation,
|
||||
@Nullable String attributeName) {
|
||||
|
||||
return annotation != null ?
|
||||
getDefaultValue(annotation.annotationType(), attributeName) :
|
||||
null;
|
||||
public static Object getDefaultValue(@Nullable Annotation annotation, @Nullable String attributeName) {
|
||||
return (annotation != null ? getDefaultValue(annotation.annotationType(), attributeName) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1115,14 +1155,12 @@ public abstract class AnnotationUtils {
|
|||
*/
|
||||
@Nullable
|
||||
public static Object getDefaultValue(
|
||||
@Nullable Class<? extends Annotation> annotationType,
|
||||
@Nullable String attributeName) {
|
||||
@Nullable Class<? extends Annotation> annotationType, @Nullable String attributeName) {
|
||||
|
||||
if (annotationType == null || !StringUtils.hasText(attributeName)) {
|
||||
return null;
|
||||
}
|
||||
return MergedAnnotation.from(annotationType)
|
||||
.getDefaultValue(attributeName).orElse(null);
|
||||
return MergedAnnotation.from(annotationType).getDefaultValue(attributeName).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1145,7 +1183,29 @@ public abstract class AnnotationUtils {
|
|||
public static <A extends Annotation> A synthesizeAnnotation(
|
||||
A annotation, @Nullable AnnotatedElement annotatedElement) {
|
||||
|
||||
return synthesizeAnnotation(annotation, (Object) annotatedElement);
|
||||
if (annotation instanceof SynthesizedAnnotation || AnnotationFilter.PLAIN.matches(annotation)) {
|
||||
return annotation;
|
||||
}
|
||||
return MergedAnnotation.from(annotatedElement, annotation).synthesize();
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>Synthesize</em> an annotation from its default attributes values.
|
||||
* <p>This method simply delegates to
|
||||
* {@link #synthesizeAnnotation(Map, Class, AnnotatedElement)},
|
||||
* supplying an empty map for the source attribute values and {@code null}
|
||||
* for the {@link AnnotatedElement}.
|
||||
* @param annotationType the type of annotation to synthesize
|
||||
* @return the synthesized annotation
|
||||
* @throws IllegalArgumentException if a required attribute is missing
|
||||
* @throws AnnotationConfigurationException if invalid configuration of
|
||||
* {@code @AliasFor} is detected
|
||||
* @since 4.2
|
||||
* @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
|
||||
* @see #synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||
*/
|
||||
public static <A extends Annotation> A synthesizeAnnotation(Class<A> annotationType) {
|
||||
return synthesizeAnnotation(Collections.emptyMap(), annotationType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1177,38 +1237,17 @@ public abstract class AnnotationUtils {
|
|||
* @see #getAnnotationAttributes(AnnotatedElement, Annotation)
|
||||
* @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
|
||||
*/
|
||||
public static <A extends Annotation> A synthesizeAnnotation(
|
||||
Map<String, Object> attributes, Class<A> annotationType,
|
||||
@Nullable AnnotatedElement annotatedElement) {
|
||||
public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object> attributes,
|
||||
Class<A> annotationType, @Nullable AnnotatedElement annotatedElement) {
|
||||
|
||||
try {
|
||||
return MergedAnnotation.from(annotatedElement, annotationType, attributes)
|
||||
.synthesize();
|
||||
return MergedAnnotation.from(annotatedElement, annotationType, attributes).synthesize();
|
||||
}
|
||||
catch (NoSuchElementException | IllegalStateException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>Synthesize</em> an annotation from its default attributes values.
|
||||
* <p>This method simply delegates to
|
||||
* {@link #synthesizeAnnotation(Map, Class, AnnotatedElement)},
|
||||
* supplying an empty map for the source attribute values and {@code null}
|
||||
* for the {@link AnnotatedElement}.
|
||||
* @param annotationType the type of annotation to synthesize
|
||||
* @return the synthesized annotation
|
||||
* @throws IllegalArgumentException if a required attribute is missing
|
||||
* @throws AnnotationConfigurationException if invalid configuration of
|
||||
* {@code @AliasFor} is detected
|
||||
* @since 4.2
|
||||
* @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
|
||||
* @see #synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||
*/
|
||||
public static <A extends Annotation> A synthesizeAnnotation(Class<A> annotationType) {
|
||||
return synthesizeAnnotation(Collections.emptyMap(), annotationType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>Synthesize</em> an array of annotations from the supplied array
|
||||
* of {@code annotations} by creating a new array of the same size and
|
||||
|
@ -1226,9 +1265,7 @@ public abstract class AnnotationUtils {
|
|||
* @see #synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||
* @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
|
||||
*/
|
||||
static Annotation[] synthesizeAnnotationArray(Annotation[] annotations,
|
||||
@Nullable Object annotatedElement) {
|
||||
|
||||
static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, AnnotatedElement annotatedElement) {
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) {
|
||||
return annotations;
|
||||
}
|
||||
|
@ -1240,16 +1277,6 @@ public abstract class AnnotationUtils {
|
|||
return synthesized;
|
||||
}
|
||||
|
||||
private static <A extends Annotation> A synthesizeAnnotation(A annotation,
|
||||
@Nullable Object annotatedElement) {
|
||||
if (annotation instanceof SynthesizedAnnotation ||
|
||||
AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement) ||
|
||||
AnnotationFilter.PLAIN.matches(annotation)) {
|
||||
return annotation;
|
||||
}
|
||||
return MergedAnnotation.from(annotatedElement, annotation).synthesize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the internal annotation metadata cache.
|
||||
* @since 4.3.15
|
||||
|
@ -1259,11 +1286,6 @@ public abstract class AnnotationUtils {
|
|||
AnnotationsScanner.clearCache();
|
||||
}
|
||||
|
||||
private static <A extends Annotation> boolean isSingleLevelPresent(
|
||||
MergedAnnotation<A> mergedAnnotation) {
|
||||
int depth = mergedAnnotation.getDepth();
|
||||
return depth == 0 || depth == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal holder used to wrap default values.
|
||||
|
@ -1280,7 +1302,6 @@ public abstract class AnnotationUtils {
|
|||
public String toString() {
|
||||
return "*" + this.defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.springframework.lang.Nullable;
|
|||
* synthesized} back into an actual {@link java.lang.annotation.Annotation}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.2
|
||||
* @param <A> the annotation type
|
||||
* @see MergedAnnotations
|
||||
|
@ -65,10 +66,10 @@ public interface MergedAnnotation<A extends Annotation> {
|
|||
|
||||
|
||||
/**
|
||||
* Return the class name of the actual annotation type.
|
||||
* Return the {@code Class} reference for the actual annotation type.
|
||||
* @return the annotation type
|
||||
*/
|
||||
String getType();
|
||||
Class<A> getType();
|
||||
|
||||
/**
|
||||
* Return if the annotation is present on the source. Considers
|
||||
|
@ -323,8 +324,7 @@ public interface MergedAnnotation<A extends Annotation> {
|
|||
* @return the value as a enum
|
||||
* @throws NoSuchElementException if there is no matching attribute
|
||||
*/
|
||||
<E extends Enum<E>> E getEnum(String attributeName, Class<E> type)
|
||||
throws NoSuchElementException;
|
||||
<E extends Enum<E>> E getEnum(String attributeName, Class<E> type) throws NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Return a required enum array attribute value from the annotation.
|
||||
|
@ -333,8 +333,7 @@ public interface MergedAnnotation<A extends Annotation> {
|
|||
* @return the value as a enum array
|
||||
* @throws NoSuchElementException if there is no matching attribute
|
||||
*/
|
||||
<E extends Enum<E>> E[] getEnumArray(String attributeName, Class<E> type)
|
||||
throws NoSuchElementException;
|
||||
<E extends Enum<E>> E[] getEnumArray(String attributeName, Class<E> type) throws NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Return a required annotation attribute value from the annotation.
|
||||
|
@ -353,8 +352,8 @@ public interface MergedAnnotation<A extends Annotation> {
|
|||
* @return the value as a {@link MergedAnnotation} array
|
||||
* @throws NoSuchElementException if there is no matching attribute
|
||||
*/
|
||||
<T extends Annotation> MergedAnnotation<T>[] getAnnotationArray(String attributeName,
|
||||
Class<T> type) throws NoSuchElementException;
|
||||
<T extends Annotation> MergedAnnotation<T>[] getAnnotationArray(String attributeName, Class<T> type)
|
||||
throws NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Return an optional attribute value from the annotation.
|
||||
|
@ -422,24 +421,21 @@ public interface MergedAnnotation<A extends Annotation> {
|
|||
MergedAnnotation<A> withNonMergedAttributes();
|
||||
|
||||
/**
|
||||
* Return an immutable {@link Map} that contains all the annotation
|
||||
* attributes. The {@link MapValues} options may be used to change the way
|
||||
* that values are added.
|
||||
* Return an immutable {@link Map} that contains all the annotation attributes.
|
||||
* The {@link MapValues} options may be used to change the way that values are added.
|
||||
* @param options map value options
|
||||
* @return a map containing the attributes and values
|
||||
* @return an immutable map containing the attributes and values
|
||||
*/
|
||||
Map<String, Object> asMap(MapValues... options);
|
||||
|
||||
/**
|
||||
* Return a {@link Map} of the supplied type that contains all the annotation
|
||||
* attributes. The {@link MapValues} options may be used to change the way
|
||||
* that values are added.
|
||||
* @param factory a map factory or {@code null} to return an immutable map.
|
||||
* Return a {@link Map} of the given type that contains all the annotation attributes.
|
||||
* The {@link MapValues} options may be used to change the way that values are added.
|
||||
* @param factory a map factory
|
||||
* @param options map value options
|
||||
* @return a map containing the attributes and values
|
||||
*/
|
||||
<T extends Map<String, Object>> T asMap(
|
||||
@Nullable Function<MergedAnnotation<?>, T> factory, MapValues... options);
|
||||
<T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options);
|
||||
|
||||
/**
|
||||
* Return a type-safe synthesized version of this annotation that can be
|
||||
|
@ -460,8 +456,7 @@ public interface MergedAnnotation<A extends Annotation> {
|
|||
* @throws NoSuchElementException on a missing annotation
|
||||
* @see MergedAnnotationPredicates
|
||||
*/
|
||||
Optional<A> synthesize(@Nullable Predicate<? super MergedAnnotation<A>> condition)
|
||||
throws NoSuchElementException;
|
||||
Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition) throws NoSuchElementException;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -511,10 +506,9 @@ public interface MergedAnnotation<A extends Annotation> {
|
|||
* Create a new {@link MergedAnnotations} instance from the specified
|
||||
* annotation type and attributes maps.
|
||||
* @param annotationType the annotation type
|
||||
* @param attributes the annotation attributes or {@code null} if just
|
||||
* default values should be used
|
||||
* @return a {@link MergedAnnotation} instance for the annotation and
|
||||
* attributes
|
||||
* @param attributes the annotation attributes or {@code null} if just default
|
||||
* values should be used
|
||||
* @return a {@link MergedAnnotation} instance for the annotation and attributes
|
||||
* @see #from(AnnotatedElement, Class, Map)
|
||||
*/
|
||||
static <A extends Annotation> MergedAnnotation<A> from(
|
||||
|
@ -530,10 +524,9 @@ public interface MergedAnnotation<A extends Annotation> {
|
|||
* information and logging. It does not need to <em>actually</em> contain
|
||||
* the specified annotations and it will not be searched.
|
||||
* @param annotationType the annotation type
|
||||
* @param attributes the annotation attributes or {@code null} if just
|
||||
* default values should be used
|
||||
* @return a {@link MergedAnnotation} instance for the annotation and
|
||||
* attributes
|
||||
* @param attributes the annotation attributes or {@code null} if just default
|
||||
* values should be used
|
||||
* @return a {@link MergedAnnotation} instance for the annotation and attributes
|
||||
*/
|
||||
static <A extends Annotation> MergedAnnotation<A> from(
|
||||
@Nullable AnnotatedElement source, Class<A> annotationType, @Nullable Map<String, ?> attributes) {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
@ -50,7 +49,7 @@ public abstract class MergedAnnotationPredicates {
|
|||
* @return a {@link Predicate} to test the annotation type
|
||||
*/
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(String... typeNames) {
|
||||
return annotation -> ObjectUtils.containsElement(typeNames, annotation.getType());
|
||||
return annotation -> ObjectUtils.containsElement(typeNames, annotation.getType().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,7 +61,7 @@ public abstract class MergedAnnotationPredicates {
|
|||
* @return a {@link Predicate} to test the annotation type
|
||||
*/
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(Class<?>... types) {
|
||||
return annotation -> Arrays.stream(types).anyMatch(type -> type.getName().equals(annotation.getType()));
|
||||
return annotation -> ObjectUtils.containsElement(types, annotation.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,7 +75,7 @@ public abstract class MergedAnnotationPredicates {
|
|||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(Collection<?> types) {
|
||||
return annotation -> types.stream()
|
||||
.map(type -> type instanceof Class ? ((Class<?>) type).getName() : type.toString())
|
||||
.anyMatch(typeName -> typeName.equals(annotation.getType()));
|
||||
.anyMatch(typeName -> typeName.equals(annotation.getType().getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,7 +140,6 @@ public abstract class MergedAnnotationPredicates {
|
|||
return ObjectUtils.nullSafeEquals(value, this.lastValue);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -165,7 +163,6 @@ public abstract class MergedAnnotationPredicates {
|
|||
K key = this.keyExtractor.apply(annotation);
|
||||
return this.seen.add(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.springframework.lang.Nullable;
|
|||
* {@link MergedAnnotation#missing()}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.2
|
||||
* @param <A> the annotation type
|
||||
*/
|
||||
|
@ -44,7 +45,7 @@ final class MissingMergedAnnotation<A extends Annotation> extends AbstractMerged
|
|||
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
public Class<A> getType() {
|
||||
throw new NoSuchElementException("Unable to get type for missing annotation");
|
||||
}
|
||||
|
||||
|
@ -112,13 +113,8 @@ final class MissingMergedAnnotation<A extends Annotation> extends AbstractMerged
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public <T extends Map<String, Object>> T asMap(
|
||||
@Nullable Function<MergedAnnotation<?>, T> factory, MapValues... options) {
|
||||
if (factory != null) {
|
||||
return factory.apply(this);
|
||||
}
|
||||
return (T) ((Map) Collections.emptyMap());
|
||||
public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options) {
|
||||
return factory.apply(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -176,7 +176,7 @@ final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> i
|
|||
Class<?> type = ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType());
|
||||
return this.annotation.getValue(name, type).orElseThrow(
|
||||
() -> new NoSuchElementException("No value found for attribute named '" + name +
|
||||
"' in merged annotation " + this.annotation.getType()));
|
||||
"' in merged annotation " + this.annotation.getType().getName()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -49,7 +49,7 @@ import org.springframework.util.ReflectionUtils;
|
|||
* return type, namely:
|
||||
*
|
||||
* <p><table border="1">
|
||||
* <tr><th >Return Type</th><th >Extracted Type</th></tr>
|
||||
* <tr><th>Return Type</th><th >Extracted Type</th></tr>
|
||||
* <tr><td>Class</td><td>Class or String</td></tr>
|
||||
* <tr><td>Class[]</td><td>Class[] or String[]</td></tr>
|
||||
* <tr><td>Annotation</td><td>Annotation, Map or Object compatible with the value
|
||||
|
@ -60,6 +60,7 @@ import org.springframework.util.ReflectionUtils;
|
|||
* </table>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.2
|
||||
* @param <A> the annotation type
|
||||
* @see TypeMappedAnnotations
|
||||
|
@ -91,17 +92,16 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
private String string;
|
||||
|
||||
|
||||
private TypeMappedAnnotation(AnnotationTypeMapping mapping,
|
||||
@Nullable Object source, @Nullable Object rootAttributes,
|
||||
BiFunction<Method, Object, Object> valueExtractor, int aggregateIndex) {
|
||||
private TypeMappedAnnotation(AnnotationTypeMapping mapping, @Nullable Object source,
|
||||
@Nullable Object rootAttributes, BiFunction<Method, Object, Object> valueExtractor,
|
||||
int aggregateIndex) {
|
||||
|
||||
this(mapping, source, rootAttributes, valueExtractor, aggregateIndex, null);
|
||||
}
|
||||
|
||||
private TypeMappedAnnotation(AnnotationTypeMapping mapping,
|
||||
@Nullable Object source, @Nullable Object rootAttributes,
|
||||
BiFunction<Method, Object, Object> valueExtractor, int aggregateIndex,
|
||||
@Nullable int[] resolvedRootMirrors) {
|
||||
private TypeMappedAnnotation(AnnotationTypeMapping mapping, @Nullable Object source,
|
||||
@Nullable Object rootAttributes, BiFunction<Method, Object, Object> valueExtractor,
|
||||
int aggregateIndex, @Nullable int[] resolvedRootMirrors) {
|
||||
|
||||
this.source = source;
|
||||
this.rootAttributes = rootAttributes;
|
||||
|
@ -110,18 +110,15 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
this.aggregateIndex = aggregateIndex;
|
||||
this.useMergedValues = true;
|
||||
this.attributeFilter = null;
|
||||
this.resolvedRootMirrors = resolvedRootMirrors != null ? resolvedRootMirrors
|
||||
: mapping.getRoot().getMirrorSets().resolve(source, rootAttributes,
|
||||
this.valueExtractor);
|
||||
this.resolvedMirrors = getDepth() == 0 ? this.resolvedRootMirrors
|
||||
: mapping.getMirrorSets().resolve(source, this,
|
||||
this::getValueForMirrorResolution);
|
||||
this.resolvedRootMirrors = (resolvedRootMirrors != null ? resolvedRootMirrors :
|
||||
mapping.getRoot().getMirrorSets().resolve(source, rootAttributes, this.valueExtractor));
|
||||
this.resolvedMirrors = (getDepth() == 0 ? this.resolvedRootMirrors :
|
||||
mapping.getMirrorSets().resolve(source, this, this::getValueForMirrorResolution));
|
||||
}
|
||||
|
||||
private TypeMappedAnnotation(AnnotationTypeMapping mapping,
|
||||
@Nullable Object source, @Nullable Object rootAnnotation,
|
||||
BiFunction<Method, Object, Object> valueExtractor, int aggregateIndex,
|
||||
boolean useMergedValues, @Nullable Predicate<String> attributeFilter,
|
||||
private TypeMappedAnnotation(AnnotationTypeMapping mapping, @Nullable Object source,
|
||||
@Nullable Object rootAnnotation, BiFunction<Method, Object, Object> valueExtractor,
|
||||
int aggregateIndex, boolean useMergedValues, @Nullable Predicate<String> attributeFilter,
|
||||
int[] resolvedRootMirrors, int[] resolvedMirrors) {
|
||||
|
||||
this.source = source;
|
||||
|
@ -136,16 +133,10 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private Object getValueForMirrorResolution(Method attribute, Object annotation) {
|
||||
int attributeIndex = this.mapping.getAttributes().indexOf(attribute);
|
||||
boolean valueAttribute = VALUE.equals(attribute.getName());
|
||||
return getValue(attributeIndex, !valueAttribute, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return getAnnotationType().getName();
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<A> getType() {
|
||||
return (Class<A>) this.mapping.getAnnotationType();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -184,8 +175,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
public boolean hasDefaultValue(String attributeName) {
|
||||
int attributeIndex = getAttributeIndex(attributeName, true);
|
||||
Object value = getValue(attributeIndex, true, false);
|
||||
return value == null || this.mapping.isEquivalentToDefaultValue(attributeIndex, value,
|
||||
this.valueExtractor);
|
||||
return (value == null || this.mapping.isEquivalentToDefaultValue(attributeIndex, value, this.valueExtractor));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -197,8 +187,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
Method attribute = this.mapping.getAttributes().get(attributeIndex);
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
Assert.isAssignable(type, attribute.getReturnType(),
|
||||
"Attribute " + attributeName + " type mismatch:");
|
||||
return (MergedAnnotation<T>) getRequiredValue(attributeIndex, Object.class);
|
||||
() -> "Attribute " + attributeName + " type mismatch:");
|
||||
return (MergedAnnotation<T>) getRequiredValue(attributeIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -212,7 +202,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
Assert.notNull(type, "Type must not be null");
|
||||
Assert.notNull(componentType, () -> "Attribute " + attributeName + " is not an array");
|
||||
Assert.isAssignable(type, componentType, () -> "Attribute " + attributeName + " component type mismatch:");
|
||||
return (MergedAnnotation<T>[]) getRequiredValue(attributeIndex, Object.class);
|
||||
return (MergedAnnotation<T>[]) getRequiredValue(attributeIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -243,55 +233,48 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Map<String, Object>> T asMap(
|
||||
@Nullable Function<MergedAnnotation<?>, T> factory, MapValues... options) {
|
||||
public Map<String, Object> asMap(MapValues... options) {
|
||||
return Collections.unmodifiableMap(asMap(mergedAnnotation -> new LinkedHashMap<>(), options));
|
||||
}
|
||||
|
||||
T map = (factory != null ? factory.apply(this) : (T) new LinkedHashMap<String, Object>());
|
||||
@Override
|
||||
public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options) {
|
||||
T map = factory.apply(this);
|
||||
Assert.state(map != null, "Factory used to create MergedAnnotation Map must not return null");
|
||||
AttributeMethods attributes = this.mapping.getAttributes();
|
||||
for (int i = 0; i < attributes.size(); i++) {
|
||||
Method attribute = attributes.get(i);
|
||||
Object value = isFiltered(attribute.getName()) ?
|
||||
null :
|
||||
getValue(i, getTypeForMapOptions(attribute, options));
|
||||
Object value = (isFiltered(attribute.getName()) ? null :
|
||||
getValue(i, getTypeForMapOptions(attribute, options)));
|
||||
if (value != null) {
|
||||
map.put(attribute.getName(),
|
||||
adaptValueForMapOptions(attribute, value, factory, options));
|
||||
adaptValueForMapOptions(attribute, value, map.getClass(), factory, options));
|
||||
}
|
||||
}
|
||||
return (factory != null) ? map : (T) Collections.unmodifiableMap(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
private Class<?> getTypeForMapOptions(Method attribute, MapValues[] options) {
|
||||
Class<?> attributeType = attribute.getReturnType();
|
||||
Class<?> componentType = attributeType.isArray() ?
|
||||
attributeType.getComponentType() :
|
||||
attributeType;
|
||||
Class<?> componentType = (attributeType.isArray() ? attributeType.getComponentType() : attributeType);
|
||||
if (MapValues.CLASS_TO_STRING.isIn(options) && componentType == Class.class) {
|
||||
return attributeType.isArray() ? String[].class : String.class;
|
||||
return (attributeType.isArray() ? String[].class : String.class);
|
||||
}
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
private <T extends Map<String, Object>> Object adaptValueForMapOptions(
|
||||
Method attribute, Object value,
|
||||
@Nullable Function<MergedAnnotation<?>, T> factory, MapValues[] options) {
|
||||
private <T extends Map<String, Object>> Object adaptValueForMapOptions(Method attribute, Object value,
|
||||
Class<?> mapType, Function<MergedAnnotation<?>, T> factory, MapValues[] options) {
|
||||
|
||||
if (value instanceof MergedAnnotation) {
|
||||
MergedAnnotation<?> annotation = (MergedAnnotation<?>) value;
|
||||
return MapValues.ANNOTATION_TO_MAP.isIn(options) ?
|
||||
annotation.asMap(factory, options) :
|
||||
annotation.synthesize();
|
||||
return (MapValues.ANNOTATION_TO_MAP.isIn(options) ?
|
||||
annotation.asMap(factory, options) : annotation.synthesize());
|
||||
}
|
||||
if (value instanceof MergedAnnotation[]) {
|
||||
MergedAnnotation<?>[] annotations = (MergedAnnotation<?>[]) value;
|
||||
if (MapValues.ANNOTATION_TO_MAP.isIn(options)) {
|
||||
Class<?> componentType = Map.class;
|
||||
if (factory != null) {
|
||||
componentType = factory.apply(this).getClass();
|
||||
}
|
||||
Object result = Array.newInstance(componentType, annotations.length);
|
||||
Object result = Array.newInstance(mapType, annotations.length);
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
Array.set(result, i, annotations[i].asMap(factory, options));
|
||||
}
|
||||
|
@ -309,7 +292,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
|
||||
@Override
|
||||
protected A createSynthesized() {
|
||||
return SynthesizedMergedAnnotationInvocationHandler.createProxy(this, getAnnotationType());
|
||||
return SynthesizedMergedAnnotationInvocationHandler.createProxy(this, getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -318,7 +301,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
if (string == null) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("@");
|
||||
builder.append(getType());
|
||||
builder.append(getType().getName());
|
||||
builder.append("(");
|
||||
for (int i = 0; i < this.mapping.getAttributes().size(); i++) {
|
||||
Method attribute = this.mapping.getAttributes().get(i);
|
||||
|
@ -360,8 +343,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
return (attributeIndex != -1 ? getValue(attributeIndex, type) : null);
|
||||
}
|
||||
|
||||
protected final <T> T getRequiredValue(int attributeIndex, Class<T> type) {
|
||||
T value = getValue(attributeIndex, type);
|
||||
private final Object getRequiredValue(int attributeIndex) {
|
||||
Object value = getValue(attributeIndex, Object.class);
|
||||
if (value == null) {
|
||||
throw new NoSuchElementException("No element at attribute index " + attributeIndex);
|
||||
}
|
||||
|
@ -379,9 +362,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private Object getValue(int attributeIndex, boolean useConventionMapping,
|
||||
boolean forMirrorResolution) {
|
||||
|
||||
private Object getValue(int attributeIndex, boolean useConventionMapping, boolean forMirrorResolution) {
|
||||
AnnotationTypeMapping mapping = this.mapping;
|
||||
if (this.useMergedValues) {
|
||||
int mappedIndex = this.mapping.getAliasMapping(attributeIndex);
|
||||
|
@ -419,6 +400,13 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
return ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object getValueForMirrorResolution(Method attribute, Object annotation) {
|
||||
int attributeIndex = this.mapping.getAttributes().indexOf(attribute);
|
||||
boolean valueAttribute = VALUE.equals(attribute.getName());
|
||||
return getValue(attributeIndex, !valueAttribute, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private <T> T adapt(Method attribute, @Nullable Object value, Class<T> type) {
|
||||
|
@ -485,21 +473,16 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
}
|
||||
if (!attributeType.isInstance(value)) {
|
||||
throw new IllegalStateException("Attribute '" + attribute.getName() +
|
||||
"' in annotation " + getType() + " should be compatible with " +
|
||||
"' in annotation " + getType().getName() + " should be compatible with " +
|
||||
attributeType.getName() + " but a " + value.getClass().getName() +
|
||||
" value was returned");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private MergedAnnotation<?> adaptToMergedAnnotation(Object value,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
AnnotationFilter filter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
AnnotationTypeMapping mapping = AnnotationTypeMappings.forAnnotationType(
|
||||
annotationType, filter).get(0);
|
||||
return new TypeMappedAnnotation<>(mapping, this.source, value,
|
||||
this.getValueExtractor(value), this.aggregateIndex);
|
||||
private MergedAnnotation<?> adaptToMergedAnnotation(Object value, Class<? extends Annotation> annotationType) {
|
||||
AnnotationTypeMapping mapping = AnnotationTypeMappings.forAnnotationType(annotationType).get(0);
|
||||
return new TypeMappedAnnotation<>(mapping, this.source, value, getValueExtractor(value), this.aggregateIndex);
|
||||
}
|
||||
|
||||
private BiFunction<Method, Object, Object> getValueExtractor(Object value) {
|
||||
|
@ -529,7 +512,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
this.mapping.getAttributes().indexOf(attributeName));
|
||||
if (attributeIndex == -1 && required) {
|
||||
throw new NoSuchElementException("No attribute named '" + attributeName +
|
||||
"' present in merged annotation " + getType());
|
||||
"' present in merged annotation " + getType().getName());
|
||||
}
|
||||
return attributeIndex;
|
||||
}
|
||||
|
@ -541,10 +524,6 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<A> getAnnotationType() {
|
||||
return (Class<A>) this.mapping.getAnnotationType();
|
||||
}
|
||||
|
||||
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
|
||||
Assert.notNull(annotation, "Annotation must not be null");
|
||||
|
@ -557,8 +536,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
|
||||
Assert.notNull(annotationType, "Annotation type must not be null");
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotationType);
|
||||
return new TypeMappedAnnotation<>(mappings.get(0), source, attributes,
|
||||
TypeMappedAnnotation::extractFromMap, 0);
|
||||
return new TypeMappedAnnotation<>(
|
||||
mappings.get(0), source, attributes, TypeMappedAnnotation::extractFromMap, 0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -23,7 +23,9 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Resource;
|
||||
|
@ -34,9 +36,9 @@ import org.junit.Test;
|
|||
import org.junit.internal.ArrayComparisonFailure;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Indexed;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
@ -631,7 +633,6 @@ public class AnnotatedElementUtilsTests {
|
|||
}
|
||||
|
||||
private AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||
Assert.notNull(annotationType, "annotationType must not be null");
|
||||
return AnnotatedElementUtils.findMergedAnnotationAttributes(element, annotationType.getName(), false, false);
|
||||
}
|
||||
|
||||
|
@ -699,6 +700,15 @@ public class AnnotatedElementUtilsTests {
|
|||
assertArrayEquals("path attribute: ", asArray("/test"), webMapping.path());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javaLangAnnotationTypeViaFindMergedAnnotation() throws Exception {
|
||||
Constructor<?> deprecatedCtor = Date.class.getConstructor(String.class);
|
||||
assertEquals(deprecatedCtor.getAnnotation(Deprecated.class),
|
||||
findMergedAnnotation(deprecatedCtor, Deprecated.class));
|
||||
assertEquals(Date.class.getAnnotation(Deprecated.class),
|
||||
findMergedAnnotation(Date.class, Deprecated.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javaxAnnotationTypeViaFindMergedAnnotation() throws Exception {
|
||||
assertEquals(ResourceHolder.class.getAnnotation(Resource.class),
|
||||
|
@ -707,17 +717,26 @@ public class AnnotatedElementUtilsTests {
|
|||
findMergedAnnotation(SpringAppConfigClass.class, Resource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullableAnnotationTypeViaFindMergedAnnotation() throws Exception {
|
||||
Method method = TransactionalServiceImpl.class.getMethod("doIt");
|
||||
assertEquals(method.getAnnotation(Resource.class),
|
||||
findMergedAnnotation(method, Resource.class));
|
||||
assertEquals(method.getAnnotation(Resource.class),
|
||||
findMergedAnnotation(method, Resource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllMergedAnnotationsOnClassWithInterface() throws Exception {
|
||||
Method m = TransactionalServiceImpl.class.getMethod("doIt");
|
||||
Set<Transactional> allMergedAnnotations = getAllMergedAnnotations(m, Transactional.class);
|
||||
Method method = TransactionalServiceImpl.class.getMethod("doIt");
|
||||
Set<Transactional> allMergedAnnotations = getAllMergedAnnotations(method, Transactional.class);
|
||||
assertTrue(allMergedAnnotations.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllMergedAnnotationsOnClassWithInterface() throws Exception {
|
||||
Method m = TransactionalServiceImpl.class.getMethod("doIt");
|
||||
Set<Transactional> allMergedAnnotations = findAllMergedAnnotations(m, Transactional.class);
|
||||
Method method = TransactionalServiceImpl.class.getMethod("doIt");
|
||||
Set<Transactional> allMergedAnnotations = findAllMergedAnnotations(method, Transactional.class);
|
||||
assertEquals(1, allMergedAnnotations.size());
|
||||
}
|
||||
|
||||
|
@ -1299,20 +1318,22 @@ public class AnnotatedElementUtilsTests {
|
|||
interface TransactionalService {
|
||||
|
||||
@Transactional
|
||||
void doIt();
|
||||
@Nullable
|
||||
Object doIt();
|
||||
}
|
||||
|
||||
class TransactionalServiceImpl implements TransactionalService {
|
||||
|
||||
@Override
|
||||
public void doIt() {
|
||||
@Nullable
|
||||
public Object doIt() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ComponentScan
|
||||
class ForAnnotationsClass {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -99,39 +99,6 @@ public class AnnotationFilterTests {
|
|||
assertThat(AnnotationFilter.NONE.matches(TestAnnotation.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pacakgesReturnsPackagesAnnotationFilter() {
|
||||
assertThat(AnnotationFilter.packages("com.example")).isInstanceOf(PackagesAnnotationFilter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mostAppropriateForCollectionReturnsPlainWhenPossible() {
|
||||
AnnotationFilter filter = AnnotationFilter.mostAppropriateFor(
|
||||
Arrays.asList(TestAnnotation.class, OtherAnnotation.class));
|
||||
assertThat(filter).isSameAs(AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mostAppropriateForCollectionWhenCantUsePlainReturnsNone() {
|
||||
AnnotationFilter filter = AnnotationFilter.mostAppropriateFor(Arrays.asList(
|
||||
TestAnnotation.class, OtherAnnotation.class, Nullable.class));
|
||||
assertThat(filter).isSameAs(AnnotationFilter.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mostAppropriateForArrayReturnsPlainWhenPossible() {
|
||||
AnnotationFilter filter = AnnotationFilter.mostAppropriateFor(
|
||||
TestAnnotation.class, OtherAnnotation.class);
|
||||
assertThat(filter).isSameAs(AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mostAppropriateForArrayWhenCantUsePlainReturnsNone() {
|
||||
AnnotationFilter filter = AnnotationFilter.mostAppropriateFor(
|
||||
TestAnnotation.class, OtherAnnotation.class, Nullable.class);
|
||||
assertThat(filter).isSameAs(AnnotationFilter.NONE);
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface TestAnnotation {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
|
@ -36,7 +37,7 @@ import org.junit.rules.ExpectedException;
|
|||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
@ -55,6 +56,7 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
|
|||
* @author Phillip Webb
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AnnotationUtilsTests {
|
||||
|
||||
@Rule
|
||||
|
@ -396,7 +398,7 @@ public class AnnotationUtilsTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void isAnnotationDeclaredLocallyForAllScenarios() throws Exception {
|
||||
public void isAnnotationDeclaredLocallyForAllScenarios() {
|
||||
// no class-level annotation
|
||||
assertFalse(isAnnotationDeclaredLocally(Transactional.class, NonAnnotatedInterface.class));
|
||||
assertFalse(isAnnotationDeclaredLocally(Transactional.class, NonAnnotatedClass.class));
|
||||
|
@ -435,6 +437,12 @@ public class AnnotationUtilsTests {
|
|||
assertFalse(isAnnotationInherited(Order.class, SubNonInheritedAnnotationClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAnnotationMetaPresentForJavaLangType() {
|
||||
assertTrue(isAnnotationMetaPresent(Order.class, Documented.class));
|
||||
assertTrue(isAnnotationMetaPresent(NonNullApi.class, Documented.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnotationAttributesWithoutAttributeAliases() {
|
||||
Component component = WebController.class.getAnnotation(Component.class);
|
||||
|
@ -540,6 +548,13 @@ public class AnnotationUtilsTests {
|
|||
assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findRepeatableAnnotation() {
|
||||
Repeatable repeatable = findAnnotation(MyRepeatable.class, Repeatable.class);
|
||||
assertNotNull(repeatable);
|
||||
assertEquals(MyRepeatableContainer.class, repeatable.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepeatableAnnotationsDeclaredOnMethod() throws Exception {
|
||||
Method method = InterfaceWithRepeated.class.getMethod("foo");
|
||||
|
@ -924,10 +939,8 @@ public class AnnotationUtilsTests {
|
|||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage(either(allOf(startsWith("Attributes map"),
|
||||
containsString("returned null for required attribute 'text'"),
|
||||
containsString("defined by annotation type ["
|
||||
+ AnnotationWithoutDefaults.class.getName() + "]"))).or(
|
||||
containsString(
|
||||
"No value found for attribute named 'text' in merged annotation")));
|
||||
containsString("defined by annotation type [" + AnnotationWithoutDefaults.class.getName() + "]"))).or(
|
||||
containsString("No value found for attribute named 'text' in merged annotation")));
|
||||
synthesizeAnnotation(attributes, AnnotationWithoutDefaults.class, null);
|
||||
}
|
||||
|
||||
|
@ -936,8 +949,8 @@ public class AnnotationUtilsTests {
|
|||
Map<String, Object> map = Collections.singletonMap(VALUE, 42L);
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage(containsString(
|
||||
"Attribute 'value' in annotation org.springframework.stereotype.Component "
|
||||
+ "should be compatible with java.lang.String but a java.lang.Long value was returned"));
|
||||
"Attribute 'value' in annotation org.springframework.stereotype.Component " +
|
||||
"should be compatible with java.lang.String but a java.lang.Long value was returned"));
|
||||
synthesizeAnnotation(map, Component.class, null);
|
||||
}
|
||||
|
||||
|
@ -1055,12 +1068,6 @@ public class AnnotationUtilsTests {
|
|||
void fromInterfaceImplementedByRoot();
|
||||
}
|
||||
|
||||
public interface NullableAnnotatedInterface {
|
||||
|
||||
@Nullable
|
||||
void fromInterfaceImplementedByRoot();
|
||||
}
|
||||
|
||||
public static class Root implements AnnotatedInterface {
|
||||
|
||||
@Order(27)
|
||||
|
|
|
@ -77,18 +77,17 @@ public class MergedAnnotationsTests {
|
|||
|
||||
@Test
|
||||
public void streamWhenFromClassWithMetaDepth1() {
|
||||
Stream<String> names = MergedAnnotations.from(TransactionalComponent.class)
|
||||
Stream<Class<?>> classes = MergedAnnotations.from(TransactionalComponent.class)
|
||||
.stream().map(MergedAnnotation::getType);
|
||||
assertThat(names).containsExactly(Transactional.class.getName(),
|
||||
Component.class.getName(), Indexed.class.getName());
|
||||
assertThat(classes).containsExactly(Transactional.class, Component.class, Indexed.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamWhenFromClassWithMetaDepth2() {
|
||||
Stream<String> names = MergedAnnotations.from(ComposedTransactionalComponent.class)
|
||||
Stream<Class<?>> classes = MergedAnnotations.from(ComposedTransactionalComponent.class)
|
||||
.stream().map(MergedAnnotation::getType);
|
||||
assertThat(names).containsExactly(TransactionalComponent.class.getName(),
|
||||
Transactional.class.getName(), Component.class.getName(), Indexed.class.getName());
|
||||
assertThat(classes).containsExactly(TransactionalComponent.class,
|
||||
Transactional.class, Component.class, Indexed.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1149,17 +1148,16 @@ public class MergedAnnotationsTests {
|
|||
|
||||
@Test
|
||||
public void getDirectWithoutAttributeAliases() {
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(WebController.class).get(
|
||||
Component.class);
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(WebController.class)
|
||||
.get(Component.class);
|
||||
assertThat(annotation.getString("value")).isEqualTo("webController");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDirectWithNestedAnnotations() {
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(
|
||||
ComponentScanClass.class).get(ComponentScan.class);
|
||||
MergedAnnotation<Filter>[] filters = annotation.getAnnotationArray(
|
||||
"excludeFilters", Filter.class);
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(ComponentScanClass.class)
|
||||
.get(ComponentScan.class);
|
||||
MergedAnnotation<Filter>[] filters = annotation.getAnnotationArray("excludeFilters", Filter.class);
|
||||
assertThat(Arrays.stream(filters).map(
|
||||
filter -> filter.getString("pattern"))).containsExactly("*Foo", "*Bar");
|
||||
}
|
||||
|
@ -1167,8 +1165,7 @@ public class MergedAnnotationsTests {
|
|||
@Test
|
||||
public void getDirectWithAttributeAliases1() throws Exception {
|
||||
Method method = WebController.class.getMethod("handleMappedWithValueAttribute");
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(method).get(
|
||||
RequestMapping.class);
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(method).get(RequestMapping.class);
|
||||
assertThat(annotation.getString("name")).isEqualTo("foo");
|
||||
assertThat(annotation.getStringArray("value")).containsExactly("/test");
|
||||
assertThat(annotation.getStringArray("path")).containsExactly("/test");
|
||||
|
@ -1177,8 +1174,7 @@ public class MergedAnnotationsTests {
|
|||
@Test
|
||||
public void getDirectWithAttributeAliases2() throws Exception {
|
||||
Method method = WebController.class.getMethod("handleMappedWithPathAttribute");
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(method).get(
|
||||
RequestMapping.class);
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(method).get(RequestMapping.class);
|
||||
assertThat(annotation.getString("name")).isEqualTo("bar");
|
||||
assertThat(annotation.getStringArray("value")).containsExactly("/test");
|
||||
assertThat(annotation.getStringArray("path")).containsExactly("/test");
|
||||
|
@ -1186,8 +1182,7 @@ public class MergedAnnotationsTests {
|
|||
|
||||
@Test
|
||||
public void getDirectWithAttributeAliasesWithDifferentValues() throws Exception {
|
||||
Method method = WebController.class.getMethod(
|
||||
"handleMappedWithDifferentPathAndValueAttributes");
|
||||
Method method = WebController.class.getMethod("handleMappedWithDifferentPathAndValueAttributes");
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(
|
||||
() -> MergedAnnotations.from(method).get(
|
||||
RequestMapping.class)).withMessageContaining(
|
||||
|
@ -1197,10 +1192,9 @@ public class MergedAnnotationsTests {
|
|||
|
||||
@Test
|
||||
public void getValueFromAnnotation() throws Exception {
|
||||
Method method = TransactionalStringGeneric.class.getMethod("something",
|
||||
Object.class);
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(method,
|
||||
SearchStrategy.EXHAUSTIVE).get(Order.class);
|
||||
Method method = TransactionalStringGeneric.class.getMethod("something", Object.class);
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE)
|
||||
.get(Order.class);
|
||||
assertThat(annotation.getInt("value")).isEqualTo(1);
|
||||
}
|
||||
|
||||
|
@ -1210,21 +1204,17 @@ public class MergedAnnotationsTests {
|
|||
assertThat(declaredAnnotations).hasSize(1);
|
||||
Annotation annotation = declaredAnnotations[0];
|
||||
MergedAnnotation<Annotation> mergedAnnotation = MergedAnnotation.from(annotation);
|
||||
assertThat(mergedAnnotation.getType()).contains("NonPublicAnnotation");
|
||||
assertThat(
|
||||
mergedAnnotation.synthesize().annotationType().getSimpleName()).isEqualTo(
|
||||
"NonPublicAnnotation");
|
||||
assertThat(mergedAnnotation.getType().getSimpleName()).isEqualTo("NonPublicAnnotation");
|
||||
assertThat(mergedAnnotation.synthesize().annotationType().getSimpleName()).isEqualTo("NonPublicAnnotation");
|
||||
assertThat(mergedAnnotation.getInt("value")).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDefaultValueFromAnnotation() throws Exception {
|
||||
Method method = TransactionalStringGeneric.class.getMethod("something",
|
||||
Object.class);
|
||||
MergedAnnotation<Order> annotation = MergedAnnotations.from(method,
|
||||
SearchStrategy.EXHAUSTIVE).get(Order.class);
|
||||
assertThat(annotation.getDefaultValue("value")).contains(
|
||||
Ordered.LOWEST_PRECEDENCE);
|
||||
Method method = TransactionalStringGeneric.class.getMethod("something", Object.class);
|
||||
MergedAnnotation<Order> annotation = MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE)
|
||||
.get(Order.class);
|
||||
assertThat(annotation.getDefaultValue("value")).contains(Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1233,7 +1223,7 @@ public class MergedAnnotationsTests {
|
|||
assertThat(declaredAnnotations).hasSize(1);
|
||||
Annotation declaredAnnotation = declaredAnnotations[0];
|
||||
MergedAnnotation<?> annotation = MergedAnnotation.from(declaredAnnotation);
|
||||
assertThat(annotation.getType()).isEqualTo(
|
||||
assertThat(annotation.getType().getName()).isEqualTo(
|
||||
"org.springframework.core.annotation.subpackage.NonPublicAnnotation");
|
||||
assertThat(annotation.getDefaultValue("value")).contains(-1);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
|
@ -78,7 +78,6 @@ public abstract class MetaAnnotationUtils {
|
|||
* @param annotationType the type of annotation to look for
|
||||
* @return the corresponding annotation descriptor if the annotation was found;
|
||||
* otherwise {@code null}
|
||||
* @see AnnotationUtils#findAnnotationDeclaringClass(Class, Class)
|
||||
* @see #findAnnotationDescriptorForTypes(Class, Class...)
|
||||
*/
|
||||
@Nullable
|
||||
|
@ -164,7 +163,6 @@ public abstract class MetaAnnotationUtils {
|
|||
* @param annotationTypes the types of annotations to look for
|
||||
* @return the corresponding annotation descriptor if one of the annotations
|
||||
* was found; otherwise {@code null}
|
||||
* @see AnnotationUtils#findAnnotationDeclaringClassForTypes(java.util.List, Class)
|
||||
* @see #findAnnotationDescriptor(Class, Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
Loading…
Reference in New Issue