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