diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index eb01ffd049..54a75d3733 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -521,9 +521,10 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean * @param ann the Autowired annotation * @return whether the annotation indicates that a dependency is required */ + @SuppressWarnings("deprecation") protected boolean determineRequiredStatus(MergedAnnotation ann) { - return determineRequiredStatus( - ann.asMap(mergedAnnotation -> new AnnotationAttributes())); + return determineRequiredStatus((AnnotationAttributes) + ann.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType()))); } /** @@ -533,7 +534,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean * or method when no beans are found. * @param ann the Autowired annotation * @return whether the annotation indicates that a dependency is required - * @deprecated since 5.2 in favor of {@link #determineRequiredStatus(MergedAnnotation)} + * @deprecated since 5.2, in favor of {@link #determineRequiredStatus(MergedAnnotation)} */ @Deprecated protected boolean determineRequiredStatus(AnnotationAttributes ann) { diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AbstractMergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/AbstractMergedAnnotation.java index 5d4bb500a7..b66be4fca7 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AbstractMergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AbstractMergedAnnotation.java @@ -18,7 +18,6 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Array; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; import java.util.function.Predicate; @@ -30,6 +29,7 @@ import org.springframework.util.Assert; * Abstract base class for {@link MergedAnnotation} implementations. * * @author Phillip Webb + * @author Juergen Hoeller * @since 5.2 * @param the annotation type */ @@ -167,19 +167,10 @@ abstract class AbstractMergedAnnotation implements MergedA } @Override - public Map asMap(MapValues... options) { - return asMap(null, options); - } - - @Override - public Optional synthesize( - @Nullable Predicate> condition) + public Optional synthesize(Predicate> condition) throws NoSuchElementException { - if (condition == null || condition.test(this)) { - return Optional.of(synthesize()); - } - return Optional.empty(); + return (condition.test(this) ? Optional.of(synthesize()) : Optional.empty()); } @Override @@ -199,7 +190,7 @@ abstract class AbstractMergedAnnotation implements MergedA T value = getAttributeValue(attributeName, type); if (value == null) { throw new NoSuchElementException("No attribute named '" + attributeName + - "' present in merged annotation " + getType()); + "' present in merged annotation " + getType().getName()); } return value; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java index 4d5312a638..cfbeafa8d8 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java @@ -21,7 +21,6 @@ import java.lang.reflect.AnnotatedElement; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashSet; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -137,9 +136,7 @@ public abstract class AnnotatedElementUtils { * @see #getMetaAnnotationTypes(AnnotatedElement, Class) * @see #hasMetaAnnotationTypes */ - public static Set getMetaAnnotationTypes(AnnotatedElement element, - String annotationName) { - + public static Set getMetaAnnotationTypes(AnnotatedElement element, String annotationName) { for (Annotation annotation : element.getAnnotations()) { if (annotation.annotationType().getName().equals(annotationName)) { return getMetaAnnotationTypes(element, annotation); @@ -148,14 +145,12 @@ public abstract class AnnotatedElementUtils { return Collections.emptySet(); } - private static Set getMetaAnnotationTypes(AnnotatedElement element, - @Nullable Annotation annotation) { - + private static Set getMetaAnnotationTypes(AnnotatedElement element, @Nullable Annotation annotation) { if (annotation == null) { return Collections.emptySet(); } return getAnnotations(annotation.annotationType()).stream() - .map(MergedAnnotation::getType) + .map(mergedAnnotation -> mergedAnnotation.getType().getName()) .collect(Collectors.toCollection(LinkedHashSet::new)); } @@ -171,11 +166,8 @@ public abstract class AnnotatedElementUtils { * @since 4.2.3 * @see #getMetaAnnotationTypes */ - public static boolean hasMetaAnnotationTypes(AnnotatedElement element, - Class annotationType) { - - return getAnnotations(element).stream(annotationType) - .anyMatch(MergedAnnotation::isMetaPresent); + public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class annotationType) { + return getAnnotations(element).stream(annotationType).anyMatch(MergedAnnotation::isMetaPresent); } /** @@ -190,11 +182,8 @@ public abstract class AnnotatedElementUtils { * @return {@code true} if a matching meta-annotation is present * @see #getMetaAnnotationTypes */ - public static boolean hasMetaAnnotationTypes(AnnotatedElement element, - String annotationName) { - - return getAnnotations(element).stream(annotationName) - .anyMatch(MergedAnnotation::isMetaPresent); + public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) { + return getAnnotations(element).stream(annotationName).anyMatch(MergedAnnotation::isMetaPresent); } /** @@ -211,9 +200,16 @@ public abstract class AnnotatedElementUtils { * @since 4.2.3 * @see #hasAnnotation(AnnotatedElement, Class) */ - public static boolean isAnnotated(AnnotatedElement element, - Class annotationType) { - + public static boolean isAnnotated(AnnotatedElement element,Class annotationType) { + // Shortcut: directly present on the element, with no processing needed? + if (AnnotationFilter.PLAIN.matches(annotationType)) { + return element.isAnnotationPresent(annotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... + if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { + return false; + } + // Exhaustive retrieval of merged annotations... return getAnnotations(element).isPresent(annotationType); } @@ -278,8 +274,8 @@ public abstract class AnnotatedElementUtils { * @see #getAllAnnotationAttributes(AnnotatedElement, String) */ @Nullable - public static AnnotationAttributes getMergedAnnotationAttributes( - AnnotatedElement element, String annotationName) { + public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, + String annotationName) { return getMergedAnnotationAttributes(element, annotationName, false, false); } @@ -311,9 +307,8 @@ public abstract class AnnotatedElementUtils { * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ @Nullable - public static AnnotationAttributes getMergedAnnotationAttributes( - AnnotatedElement element, String annotationName, boolean classValuesAsString, - boolean nestedAnnotationsAsMap) { + public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, + String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { MergedAnnotation mergedAnnotation = getAnnotations(element) .get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared()); @@ -328,30 +323,23 @@ public abstract class AnnotatedElementUtils { * the result back into an annotation of the specified {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. - *

This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, Class)} - * and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}. * @param element the annotated element * @param annotationType the annotation type to find * @return the merged, synthesized {@code Annotation}, or {@code null} if not found * @since 4.2 - * @see #getMergedAnnotationAttributes(AnnotatedElement, Class) * @see #findMergedAnnotation(AnnotatedElement, Class) - * @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement) */ @Nullable - public static A getMergedAnnotation(AnnotatedElement element, - Class annotationType) { - - if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { - return null; - } - A annotation = AnnotationsScanner.getDeclaredAnnotation(element, annotationType); - if (annotation != null) { - return AnnotationUtils.synthesizeAnnotation(annotation, element); - } + public static A getMergedAnnotation(AnnotatedElement element, Class annotationType) { + // Shortcut: directly present on the element, with no merging needed? + if (AnnotationFilter.PLAIN.matches(annotationType)) { + return element.getDeclaredAnnotation(annotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return null; } + // Exhaustive retrieval of merged annotations... return getAnnotations(element) .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()) .synthesize(MergedAnnotation::isPresent).orElse(null); @@ -515,9 +503,8 @@ public abstract class AnnotatedElementUtils { * attributes from all annotations found, or {@code null} if not found */ @Nullable - public static MultiValueMap getAllAnnotationAttributes( - AnnotatedElement element, String annotationName, - final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { + public static MultiValueMap getAllAnnotationAttributes(AnnotatedElement element, + String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap); return getAnnotations(element).stream(annotationName) @@ -540,16 +527,16 @@ public abstract class AnnotatedElementUtils { * @since 4.3 * @see #isAnnotated(AnnotatedElement, Class) */ - public static boolean hasAnnotation(AnnotatedElement element, - Class annotationType) { - + public static boolean hasAnnotation(AnnotatedElement element, Class annotationType) { // Shortcut: directly present on the element, with no processing needed? + if (AnnotationFilter.PLAIN.matches(annotationType)) { + return element.isAnnotationPresent(annotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return false; } - if (AnnotationsScanner.getDeclaredAnnotation(element, annotationType) != null) { - return true; - } + // Exhaustive retrieval of merged annotations... return findAnnotations(element).isPresent(annotationType); } @@ -581,9 +568,8 @@ public abstract class AnnotatedElementUtils { * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ @Nullable - public static AnnotationAttributes findMergedAnnotationAttributes( - AnnotatedElement element, Class annotationType, - boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, + Class annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { MergedAnnotation mergedAnnotation = findAnnotations(element) .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()); @@ -618,9 +604,8 @@ public abstract class AnnotatedElementUtils { * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ @Nullable - public static AnnotationAttributes findMergedAnnotationAttributes( - AnnotatedElement element, String annotationName, boolean classValuesAsString, - boolean nestedAnnotationsAsMap) { + public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, + String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { MergedAnnotation mergedAnnotation = findAnnotations(element) .get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared()); @@ -646,16 +631,16 @@ public abstract class AnnotatedElementUtils { * @see #getMergedAnnotationAttributes(AnnotatedElement, Class) */ @Nullable - public static A findMergedAnnotation(AnnotatedElement element, - Class annotationType) { - - A annotation = AnnotationsScanner.getDeclaredAnnotation(element, annotationType); - if (annotation != null) { - return AnnotationUtils.synthesizeAnnotation(annotation, element); + public static A findMergedAnnotation(AnnotatedElement element, Class annotationType) { + // Shortcut: directly present on the element, with no merging needed? + if (AnnotationFilter.PLAIN.matches(annotationType)) { + return element.getDeclaredAnnotation(annotationType); } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return null; } + // Exhaustive retrieval of merged annotations... return findAnnotations(element) .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()) .synthesize(MergedAnnotation::isPresent).orElse(null); @@ -680,9 +665,7 @@ public abstract class AnnotatedElementUtils { * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #getAllMergedAnnotations(AnnotatedElement, Class) */ - public static Set findAllMergedAnnotations( - AnnotatedElement element, Class annotationType) { - + public static Set findAllMergedAnnotations(AnnotatedElement element, Class annotationType) { return findAnnotations(element).stream(annotationType) .sorted(highAggregateIndexesFirst()) .collect(MergedAnnotationCollectors.toAnnotationSet()); @@ -706,9 +689,7 @@ public abstract class AnnotatedElementUtils { * @since 5.1 * @see #findAllMergedAnnotations(AnnotatedElement, Class) */ - public static Set findAllMergedAnnotations(AnnotatedElement element, - Set> annotationTypes) { - + public static Set findAllMergedAnnotations(AnnotatedElement element, Set> annotationTypes) { return findAnnotations(element).stream() .filter(MergedAnnotationPredicates.typeIn(annotationTypes)) .sorted(highAggregateIndexesFirst()) @@ -739,8 +720,8 @@ public abstract class AnnotatedElementUtils { * @see #findAllMergedAnnotations(AnnotatedElement, Class) * @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class) */ - public static Set findMergedRepeatableAnnotations( - AnnotatedElement element, Class annotationType) { + public static Set findMergedRepeatableAnnotations(AnnotatedElement element, + Class annotationType) { return findMergedRepeatableAnnotations(element, annotationType, null); } @@ -771,9 +752,8 @@ public abstract class AnnotatedElementUtils { * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #findAllMergedAnnotations(AnnotatedElement, Class) */ - public static Set findMergedRepeatableAnnotations( - AnnotatedElement element, Class annotationType, - @Nullable Class containerType) { + public static Set findMergedRepeatableAnnotations(AnnotatedElement element, + Class annotationType, @Nullable Class containerType) { return findRepeatableAnnotations(element, containerType, annotationType) .stream(annotationType) @@ -783,43 +763,40 @@ public abstract class AnnotatedElementUtils { private static MergedAnnotations getAnnotations(AnnotatedElement element) { return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, - RepeatableContainers.none(), AnnotationUtils.JAVA_LANG_ANNOTATION_FILTER); + RepeatableContainers.none(), AnnotationFilter.PLAIN); } private static MergedAnnotations getRepeatableAnnotations(AnnotatedElement element, - @Nullable Class containerType, - Class annotationType) { + @Nullable Class containerType, Class annotationType) { RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType); return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, - repeatableContainers, AnnotationUtils.JAVA_LANG_ANNOTATION_FILTER); + repeatableContainers, AnnotationFilter.PLAIN); } private static MergedAnnotations findAnnotations(AnnotatedElement element) { return MergedAnnotations.from(element, SearchStrategy.EXHAUSTIVE, - RepeatableContainers.none(), AnnotationUtils.JAVA_LANG_ANNOTATION_FILTER); + RepeatableContainers.none(), AnnotationFilter.PLAIN); } private static MergedAnnotations findRepeatableAnnotations(AnnotatedElement element, - @Nullable Class containerType, - Class annotationType) { + @Nullable Class containerType, Class annotationType) { RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType); return MergedAnnotations.from(element, SearchStrategy.EXHAUSTIVE, - repeatableContainers, AnnotationUtils.JAVA_LANG_ANNOTATION_FILTER); + repeatableContainers, AnnotationFilter.PLAIN); } private static Object parentAndType(MergedAnnotation annotation) { if (annotation.getParent() == null) { - return annotation.getType(); + return annotation.getType().getName(); } - return annotation.getParent().getType() + ":" + annotation.getParent().getType(); + return annotation.getParent().getType().getName() + ":" + annotation.getParent().getType().getName(); } @Nullable - private static MultiValueMap nullIfEmpty( - MultiValueMap map) { - return map.isEmpty() ? null : map; + private static MultiValueMap nullIfEmpty(MultiValueMap map) { + return (map.isEmpty() ? null : map); } private static Comparator> highAggregateIndexesFirst() { @@ -834,7 +811,7 @@ public abstract class AnnotatedElementUtils { if (!annotation.isPresent()) { return null; } - return annotation.asMap(mergedAnnotation -> new AnnotationAttributes(), + return annotation.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType()), MapValues.of(classValuesAsString, nestedAnnotationsAsMap)); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationFilter.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationFilter.java index a5e27dd3f0..b86a4b1de4 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationFilter.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationFilter.java @@ -17,10 +17,6 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; -import java.util.Arrays; -import java.util.Collection; - -import org.springframework.util.Assert; /** * Callback interface that can be used to filter specific annotation types. @@ -96,52 +92,4 @@ public interface AnnotationFilter { return new PackagesAnnotationFilter(packages); } - /** - * Return an {@link AnnotationFilter} that is the most appropriate for, and - * will always match the given annotation type. Whenever possible, - * {@link AnnotationFilter#PLAIN} will be returned. - * @param annotationType the annotation type to check - * @return the most appropriate annotation filter - */ - static AnnotationFilter mostAppropriateFor(Class annotationType) { - return (PLAIN.matches(annotationType) ? NONE : PLAIN); - } - - /** - * Return an {@link AnnotationFilter} that is the most appropriate for, and - * will always match all the given annotation types. Whenever possible, - * {@link AnnotationFilter#PLAIN} will be returned. - * @param annotationTypes the annotation types to check - * @return the most appropriate annotation filter - */ - static AnnotationFilter mostAppropriateFor(Class... annotationTypes) { - return mostAppropriateFor(Arrays.asList(annotationTypes)); - } - - /** - * Return an {@link AnnotationFilter} that is the most appropriate for, and - * will always match all the given annotation types. Whenever possible, - * {@link AnnotationFilter#PLAIN} will be returned. - * @param annotationTypes the annotation types to check (may be class names - * or class types) - * @return the most appropriate annotation filter - */ - @SuppressWarnings("unchecked") - static AnnotationFilter mostAppropriateFor(Collection annotationTypes) { - for (Object annotationType : annotationTypes) { - if (annotationType == null) { - continue; - } - Assert.isTrue(annotationType instanceof Class || annotationType instanceof String, - "AnnotationType must be a Class or String"); - if (annotationType instanceof Class && PLAIN.matches((Class) annotationType)) { - return NONE; - } - if (annotationType instanceof String && PLAIN.matches((String) annotationType)) { - return NONE; - } - } - return PLAIN; - } - } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java index 4c3fd6afa0..966293f45d 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java @@ -55,8 +55,7 @@ final class AnnotationTypeMappings { private final List mappings; - private AnnotationTypeMappings(AnnotationFilter filter, - Class annotationType) { + private AnnotationTypeMappings(AnnotationFilter filter, Class annotationType) { this.filter = filter; this.mappings = new ArrayList<>(); addAllMappings(annotationType); @@ -97,27 +96,23 @@ final class AnnotationTypeMappings { } } - private void addIfPossible(Deque queue, - AnnotationTypeMapping parent, Annotation annotation) { - addIfPossible(queue, parent, annotation.annotationType(), annotation); + private void addIfPossible(Deque queue, AnnotationTypeMapping parent, Annotation ann) { + addIfPossible(queue, parent, ann.annotationType(), ann); } - private void addIfPossible(Deque queue, - @Nullable AnnotationTypeMapping parent, - Class annotationType, @Nullable Annotation annotation) { + private void addIfPossible(Deque queue, @Nullable AnnotationTypeMapping parent, + Class annotationType, @Nullable Annotation ann) { try { - queue.addLast(new AnnotationTypeMapping(parent, annotationType, annotation)); + queue.addLast(new AnnotationTypeMapping(parent, annotationType, ann)); } catch (Exception ex) { if (ex instanceof AnnotationConfigurationException) { throw (AnnotationConfigurationException) ex; } if (failureLogger.isEnabled()) { - failureLogger.log( - "Failed to introspect meta-annotation " - + annotationType.getName(), - (parent != null) ? parent.getAnnotationType() : null, ex); + failureLogger.log("Failed to introspect meta-annotation " + annotationType.getName(), + (parent != null ? parent.getAnnotationType() : null), ex); } } } @@ -167,7 +162,7 @@ final class AnnotationTypeMappings { * @return type mappings for the annotation type */ static AnnotationTypeMappings forAnnotationType(Class annotationType) { - return forAnnotationType(annotationType, AnnotationFilter.mostAppropriateFor(annotationType)); + return forAnnotationType(annotationType, AnnotationFilter.PLAIN); } /** @@ -197,7 +192,6 @@ final class AnnotationTypeMappings { private final Map, AnnotationTypeMappings> mappings; - /** * Create a cache instance with the specified filter. * @param filter the annotation filter @@ -207,7 +201,6 @@ final class AnnotationTypeMappings { this.mappings = new ConcurrentReferenceHashMap<>(); } - /** * Return or create {@link AnnotationTypeMappings} for the specified * annotation type. @@ -218,8 +211,7 @@ final class AnnotationTypeMappings { return this.mappings.computeIfAbsent(annotationType, this::createMappings); } - AnnotationTypeMappings createMappings( - Class annotationType) { + AnnotationTypeMappings createMappings(Class annotationType) { return new AnnotationTypeMappings(this.filter, annotationType); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 7d0aecefff..5be15ff6ac 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -36,7 +36,6 @@ import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.Mirr import org.springframework.core.annotation.MergedAnnotation.MapValues; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -51,12 +50,12 @@ import org.springframework.util.StringUtils; * *

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

Terminology

* The terms directly present, indirectly present, and @@ -111,14 +110,13 @@ public abstract class AnnotationUtils { */ public static final String VALUE = MergedAnnotation.VALUE; - - static final AnnotationFilter JAVA_LANG_ANNOTATION_FILTER = + private static final AnnotationFilter JAVA_LANG_ANNOTATION_FILTER = AnnotationFilter.packages("java.lang.annotation"); - private static Map, Map> defaultValuesCache = new ConcurrentReferenceHashMap<>(); + /** * Determine whether the given class is a candidate for carrying one of the specified * annotations (at type, method or field level). @@ -180,10 +178,18 @@ public abstract class AnnotationUtils { * @return the first matching annotation, or {@code null} if not found * @since 4.0 */ + @SuppressWarnings("unchecked") @Nullable - public static A getAnnotation(Annotation annotation, - Class annotationType) { - + public static A getAnnotation(Annotation annotation, Class annotationType) { + // Shortcut: directly present on the element, with no merging needed? + if (annotationType.isInstance(annotation)) { + return synthesizeAnnotation((A) annotation, annotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... + if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotation)) { + return null; + } + // Exhaustive retrieval of merged annotations... return MergedAnnotations.from(annotation) .get(annotationType).withNonMergedAttributes() .synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null); @@ -202,15 +208,27 @@ public abstract class AnnotationUtils { * @since 3.1 */ @Nullable - public static A getAnnotation( - AnnotatedElement annotatedElement, Class annotationType) { - + public static A getAnnotation(AnnotatedElement annotatedElement, Class annotationType) { + // Shortcut: directly present on the element, with no merging needed? + if (AnnotationFilter.PLAIN.matches(annotationType)) { + return annotatedElement.getAnnotation(annotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... + if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) { + return null; + } + // Exhaustive retrieval of merged annotations... return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none(), AnnotationFilter.PLAIN) .get(annotationType).withNonMergedAttributes() .synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null); } + private static boolean isSingleLevelPresent(MergedAnnotation mergedAnnotation) { + int depth = mergedAnnotation.getDepth(); + return (depth == 0 || depth == 1); + } + /** * Get a single {@link Annotation} of {@code annotationType} from the * supplied {@link Method}, where the annotation is either present @@ -226,13 +244,9 @@ public abstract class AnnotationUtils { * @see #getAnnotation(AnnotatedElement, Class) */ @Nullable - public static A getAnnotation(Method method, - Class annotationType) { - - return MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS, - RepeatableContainers.none(), AnnotationFilter.PLAIN) - .get(annotationType).withNonMergedAttributes() - .synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null); + public static A getAnnotation(Method method, Class annotationType) { + Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); + return getAnnotation((AnnotatedElement) resolvedMethod, annotationType); } /** @@ -245,14 +259,18 @@ public abstract class AnnotationUtils { * failed to resolve at runtime) * @since 4.0.8 * @see AnnotatedElement#getAnnotations() + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ + @Deprecated @Nullable public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) { - return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS, - RepeatableContainers.none(), AnnotationFilter.NONE).stream() - .filter(MergedAnnotation::isDirectlyPresent) - .map(MergedAnnotation::withNonMergedAttributes) - .collect(MergedAnnotationCollectors.toAnnotationArray()); + try { + return synthesizeAnnotationArray(annotatedElement.getAnnotations(), annotatedElement); + } + catch (Throwable ex) { + handleIntrospectionFailure(annotatedElement, ex); + return null; + } } /** @@ -266,14 +284,18 @@ public abstract class AnnotationUtils { * failed to resolve at runtime) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) * @see AnnotatedElement#getAnnotations() + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ + @Deprecated @Nullable public static Annotation[] getAnnotations(Method method) { - return MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS, - RepeatableContainers.none(), AnnotationFilter.NONE).stream() - .filter(MergedAnnotation::isDirectlyPresent) - .map(MergedAnnotation::withNonMergedAttributes) - .collect(MergedAnnotationCollectors.toAnnotationArray()); + try { + return synthesizeAnnotationArray(BridgeMethodResolver.findBridgedMethod(method).getAnnotations(), method); + } + catch (Throwable ex) { + handleIntrospectionFailure(method, ex); + return null; + } } /** @@ -302,9 +324,11 @@ public abstract class AnnotationUtils { * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ - public static Set getRepeatableAnnotations( - AnnotatedElement annotatedElement, Class annotationType) { + @Deprecated + public static Set getRepeatableAnnotations(AnnotatedElement annotatedElement, + Class annotationType) { return getRepeatableAnnotations(annotatedElement, annotationType, null); } @@ -338,17 +362,17 @@ public abstract class AnnotationUtils { * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ - public static Set getRepeatableAnnotations( - AnnotatedElement annotatedElement, Class annotationType, - @Nullable Class containerAnnotationType) { + @Deprecated + public static Set getRepeatableAnnotations(AnnotatedElement annotatedElement, + Class annotationType, @Nullable Class containerAnnotationType) { - RepeatableContainers repeatableContainers = containerAnnotationType != null ? + RepeatableContainers repeatableContainers = (containerAnnotationType != null ? RepeatableContainers.of(annotationType, containerAnnotationType) : - RepeatableContainers.standardRepeatables(); - AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType); + RepeatableContainers.standardRepeatables()); return MergedAnnotations.from(annotatedElement, SearchStrategy.SUPER_CLASS, - repeatableContainers, annotationFilter) + repeatableContainers, AnnotationFilter.PLAIN) .stream(annotationType) .filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex)) .map(MergedAnnotation::withNonMergedAttributes) @@ -382,9 +406,11 @@ public abstract class AnnotationUtils { * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ - public static Set getDeclaredRepeatableAnnotations( - AnnotatedElement annotatedElement, Class annotationType) { + @Deprecated + public static Set getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, + Class annotationType) { return getDeclaredRepeatableAnnotations(annotatedElement, annotationType, null); } @@ -418,18 +444,17 @@ public abstract class AnnotationUtils { * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ - public static Set getDeclaredRepeatableAnnotations( - AnnotatedElement annotatedElement, Class annotationType, - @Nullable Class containerAnnotationType) { + @Deprecated + public static Set getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, + Class annotationType, @Nullable Class containerAnnotationType) { RepeatableContainers repeatableContainers = containerAnnotationType != null ? RepeatableContainers.of(annotationType, containerAnnotationType) : RepeatableContainers.standardRepeatables(); - AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor( - annotationType, containerAnnotationType); return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT, - repeatableContainers, annotationFilter).stream(annotationType) + repeatableContainers, AnnotationFilter.PLAIN).stream(annotationType) .map(MergedAnnotation::withNonMergedAttributes) .collect(MergedAnnotationCollectors.toAnnotationSet()); } @@ -452,11 +477,21 @@ public abstract class AnnotationUtils { */ @Nullable public static A findAnnotation( - AnnotatedElement annotatedElement, Class annotationType) { + AnnotatedElement annotatedElement, @Nullable Class annotationType) { - AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType); - return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT, - RepeatableContainers.none(), annotationFilter) + if (annotationType == null) { + return null; + } + // Shortcut: directly present on the element, with no merging needed? + if (AnnotationFilter.PLAIN.matches(annotationType)) { + return annotatedElement.getDeclaredAnnotation(annotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... + if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) { + return null; + } + // Exhaustive retrieval of merged annotations... + return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS) .get(annotationType).withNonMergedAttributes() .synthesize(MergedAnnotation::isPresent).orElse(null); } @@ -481,10 +516,16 @@ public abstract class AnnotationUtils { if (annotationType == null) { return null; } - - AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType); - return MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE, - RepeatableContainers.none(), annotationFilter) + // Shortcut: directly present on the element, with no merging needed? + if (AnnotationFilter.PLAIN.matches(annotationType)) { + return method.getDeclaredAnnotation(annotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... + if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(method)) { + return null; + } + // Exhaustive retrieval of merged annotations... + return MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE) .get(annotationType).withNonMergedAttributes() .synthesize(MergedAnnotation::isPresent).orElse(null); } @@ -512,10 +553,20 @@ public abstract class AnnotationUtils { * @return the first matching annotation, or {@code null} if not found */ @Nullable - public static A findAnnotation(Class clazz, Class annotationType) { - AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType); - return MergedAnnotations.from(clazz, SearchStrategy.EXHAUSTIVE, - RepeatableContainers.none(), annotationFilter) + public static A findAnnotation(Class clazz, @Nullable Class annotationType) { + if (annotationType == null) { + return null; + } + // Shortcut: directly present on the element, with no merging needed? + if (AnnotationFilter.PLAIN.matches(annotationType)) { + return clazz.getDeclaredAnnotation(annotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... + if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) { + return null; + } + // Exhaustive retrieval of merged annotations... + return MergedAnnotations.from(clazz, SearchStrategy.EXHAUSTIVE) .get(annotationType).withNonMergedAttributes() .synthesize(MergedAnnotation::isPresent).orElse(null); } @@ -539,9 +590,9 @@ public abstract class AnnotationUtils { * or {@code null} if not found * @see Class#isAnnotationPresent(Class) * @see Class#getDeclaredAnnotations() - * @see #findAnnotationDeclaringClassForTypes(List, Class) - * @see #isAnnotationDeclaredLocally(Class, Class) + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ + @Deprecated @Nullable public static Class findAnnotationDeclaringClass( Class annotationType, @Nullable Class clazz) { @@ -550,9 +601,7 @@ public abstract class AnnotationUtils { return null; } - AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType); - return (Class) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS, - RepeatableContainers.none(), annotationFilter) + return (Class) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS) .get(annotationType, MergedAnnotation::isDirectlyPresent) .getSource(); } @@ -578,9 +627,9 @@ public abstract class AnnotationUtils { * @since 3.2.2 * @see Class#isAnnotationPresent(Class) * @see Class#getDeclaredAnnotations() - * @see #findAnnotationDeclaringClass(Class, Class) - * @see #isAnnotationDeclaredLocally(Class, Class) + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ + @Deprecated @Nullable public static Class findAnnotationDeclaringClassForTypes( List> annotationTypes, @Nullable Class clazz) { @@ -589,9 +638,7 @@ public abstract class AnnotationUtils { return null; } - AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationTypes); - return (Class) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS, - RepeatableContainers.none(), annotationFilter) + return (Class) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS) .stream() .filter(MergedAnnotationPredicates.typeIn(annotationTypes).and(MergedAnnotation::isDirectlyPresent)) .map(MergedAnnotation::getSource) @@ -605,16 +652,13 @@ public abstract class AnnotationUtils { *

The supplied {@link Class} may represent any type. *

Meta-annotations will not be searched. *

Note: This method does not determine if the annotation - * is {@linkplain java.lang.annotation.Inherited inherited}. For greater - * clarity regarding inherited annotations, consider using - * {@link #isAnnotationInherited(Class, Class)} instead. + * is {@linkplain java.lang.annotation.Inherited inherited}. * @param annotationType the annotation type to look for * @param clazz the class to check for the annotation on * @return {@code true} if an annotation of the specified {@code annotationType} * is directly present * @see java.lang.Class#getDeclaredAnnotations() * @see java.lang.Class#getDeclaredAnnotation(Class) - * @see #isAnnotationInherited(Class, Class) */ public static boolean isAnnotationDeclaredLocally(Class annotationType, Class clazz) { return MergedAnnotations.from(clazz).get(annotationType).isDirectlyPresent(); @@ -638,7 +682,9 @@ public abstract class AnnotationUtils { * is present and inherited * @see Class#isAnnotationPresent(Class) * @see #isAnnotationDeclaredLocally(Class, Class) + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ + @Deprecated public static boolean isAnnotationInherited(Class annotationType, Class clazz) { return MergedAnnotations.from(clazz, SearchStrategy.INHERITED_ANNOTATIONS) .stream(annotationType) @@ -654,11 +700,27 @@ public abstract class AnnotationUtils { * @param metaAnnotationType the type of meta-annotation to search for * @return {@code true} if such an annotation is meta-present * @since 4.2.1 + * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API */ + @Deprecated public static boolean isAnnotationMetaPresent(Class annotationType, @Nullable Class metaAnnotationType) { - return MergedAnnotations.from(annotationType, SearchStrategy.EXHAUSTIVE).isPresent(annotationType); + if (metaAnnotationType == null) { + return false; + } + // Shortcut: directly present on the element, with no merging needed? + if (AnnotationFilter.PLAIN.matches(annotationType) || + AnnotationFilter.PLAIN.matches(metaAnnotationType)) { + return annotationType.isAnnotationPresent(metaAnnotationType); + } + // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... + if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotationType)) { + return false; + } + // Exhaustive retrieval of merged annotations... + return (MergedAnnotations.from( + annotationType, SearchStrategy.INHERITED_ANNOTATIONS).isPresent(metaAnnotationType)); } /** @@ -731,8 +793,8 @@ public abstract class AnnotationUtils { * corresponding attribute values as values (never {@code null}) * @see #getAnnotationAttributes(Annotation, boolean, boolean) */ - public static Map getAnnotationAttributes(Annotation annotation, - boolean classValuesAsString) { + public static Map getAnnotationAttributes( + Annotation annotation, boolean classValuesAsString) { return getAnnotationAttributes(annotation, classValuesAsString, false); } @@ -753,11 +815,10 @@ public abstract class AnnotationUtils { * and corresponding attribute values as values (never {@code null}) * @since 3.1.1 */ - public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, - boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + public static AnnotationAttributes getAnnotationAttributes( + Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { - return getAnnotationAttributes(null, annotation, classValuesAsString, - nestedAnnotationsAsMap); + return getAnnotationAttributes(null, annotation, classValuesAsString, nestedAnnotationsAsMap); } /** @@ -801,22 +862,14 @@ public abstract class AnnotationUtils { @Nullable AnnotatedElement annotatedElement, Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { - ClassLoader classLoader = annotation.annotationType().getClassLoader(); MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap); return MergedAnnotation.from(annotatedElement, annotation) .withNonMergedAttributes() - .asMap(getAnnotationAttributesFactory(classLoader), mapValues); + .asMap(getAnnotationAttributesFactory(), mapValues); } - @SuppressWarnings("unchecked") - private static Function, AnnotationAttributes> getAnnotationAttributesFactory( - ClassLoader classLoader) { - - return annotation -> { - Class type = (Class) ClassUtils - .resolveClassName(annotation.getType(), classLoader); - return new AnnotationAttributes(type, true); - }; + private static Function, AnnotationAttributes> getAnnotationAttributesFactory() { + return (annotation -> new AnnotationAttributes(annotation.getType(), true)); } /** @@ -861,9 +914,8 @@ public abstract class AnnotationUtils { } else { // If we have nested annotations, we need them as nested maps - AnnotationAttributes attributes = MergedAnnotation.from(annotationType).asMap( - getAnnotationAttributesFactory(annotationType.getClassLoader()), - MapValues.ANNOTATION_TO_MAP); + AnnotationAttributes attributes = MergedAnnotation.from(annotationType) + .asMap(getAnnotationAttributesFactory(), MapValues.ANNOTATION_TO_MAP); for (Map.Entry element : attributes.entrySet()) { result.put(element.getKey(), new DefaultValueHolder(element.getValue())); } @@ -927,18 +979,14 @@ public abstract class AnnotationUtils { } } - private static Object getAttributeValueForMirrorResolution(Method attribute, - Object attributes) { - + private static Object getAttributeValueForMirrorResolution(Method attribute, Object attributes) { Object result = ((AnnotationAttributes) attributes).get(attribute.getName()); - return result instanceof DefaultValueHolder ? - ((DefaultValueHolder) result).defaultValue : - result; + return (result instanceof DefaultValueHolder ? ((DefaultValueHolder) result).defaultValue : result); } @Nullable - private static Object adaptValue(@Nullable Object annotatedElement, - @Nullable Object value, boolean classValuesAsString) { + private static Object adaptValue( + @Nullable Object annotatedElement, @Nullable Object value, boolean classValuesAsString) { if (classValuesAsString) { if (value instanceof Class) { @@ -962,8 +1010,7 @@ public abstract class AnnotationUtils { Annotation[] synthesized = (Annotation[]) Array.newInstance( annotations.getClass().getComponentType(), annotations.length); for (int i = 0; i < annotations.length; i++) { - synthesized[i] = MergedAnnotation.from(annotatedElement, annotations[i]) - .synthesize(); + synthesized[i] = MergedAnnotation.from(annotatedElement, annotations[i]).synthesize(); } return synthesized; } @@ -994,9 +1041,7 @@ public abstract class AnnotationUtils { * @see #getValue(Annotation) */ @Nullable - public static Object getValue(@Nullable Annotation annotation, - @Nullable String attributeName) { - + public static Object getValue(@Nullable Annotation annotation, @Nullable String attributeName) { if (annotation == null || !StringUtils.hasText(attributeName)) { return null; } @@ -1047,8 +1092,7 @@ public abstract class AnnotationUtils { * @param ex the exception that we encountered * @see #rethrowAnnotationConfigurationException */ - private static void handleIntrospectionFailure(@Nullable AnnotatedElement element, - Throwable ex) { + private static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) { rethrowAnnotationConfigurationException(ex); IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO; boolean meta = false; @@ -1085,12 +1129,8 @@ public abstract class AnnotationUtils { * @see #getDefaultValue(Class, String) */ @Nullable - public static Object getDefaultValue(@Nullable Annotation annotation, - @Nullable String attributeName) { - - return annotation != null ? - getDefaultValue(annotation.annotationType(), attributeName) : - null; + public static Object getDefaultValue(@Nullable Annotation annotation, @Nullable String attributeName) { + return (annotation != null ? getDefaultValue(annotation.annotationType(), attributeName) : null); } /** @@ -1115,14 +1155,12 @@ public abstract class AnnotationUtils { */ @Nullable public static Object getDefaultValue( - @Nullable Class annotationType, - @Nullable String attributeName) { + @Nullable Class annotationType, @Nullable String attributeName) { if (annotationType == null || !StringUtils.hasText(attributeName)) { return null; } - return MergedAnnotation.from(annotationType) - .getDefaultValue(attributeName).orElse(null); + return MergedAnnotation.from(annotationType).getDefaultValue(attributeName).orElse(null); } /** @@ -1145,7 +1183,29 @@ public abstract class AnnotationUtils { public static A synthesizeAnnotation( A annotation, @Nullable AnnotatedElement annotatedElement) { - return synthesizeAnnotation(annotation, (Object) annotatedElement); + if (annotation instanceof SynthesizedAnnotation || AnnotationFilter.PLAIN.matches(annotation)) { + return annotation; + } + return MergedAnnotation.from(annotatedElement, annotation).synthesize(); + } + + /** + * Synthesize an annotation from its default attributes values. + *

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 synthesizeAnnotation(Class annotationType) { + return synthesizeAnnotation(Collections.emptyMap(), annotationType, null); } /** @@ -1177,38 +1237,17 @@ public abstract class AnnotationUtils { * @see #getAnnotationAttributes(AnnotatedElement, Annotation) * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) */ - public static A synthesizeAnnotation( - Map attributes, Class annotationType, - @Nullable AnnotatedElement annotatedElement) { + public static A synthesizeAnnotation(Map attributes, + Class annotationType, @Nullable AnnotatedElement annotatedElement) { try { - return MergedAnnotation.from(annotatedElement, annotationType, attributes) - .synthesize(); + return MergedAnnotation.from(annotatedElement, annotationType, attributes).synthesize(); } catch (NoSuchElementException | IllegalStateException ex) { throw new IllegalArgumentException(ex); } } - /** - * Synthesize an annotation from its default attributes values. - *

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 synthesizeAnnotation(Class annotationType) { - return synthesizeAnnotation(Collections.emptyMap(), annotationType, null); - } - /** * Synthesize an array of annotations from the supplied array * of {@code annotations} by creating a new array of the same size and @@ -1226,9 +1265,7 @@ public abstract class AnnotationUtils { * @see #synthesizeAnnotation(Annotation, AnnotatedElement) * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) */ - static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, - @Nullable Object annotatedElement) { - + static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, AnnotatedElement annotatedElement) { if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) { return annotations; } @@ -1240,16 +1277,6 @@ public abstract class AnnotationUtils { return synthesized; } - private static A synthesizeAnnotation(A annotation, - @Nullable Object annotatedElement) { - if (annotation instanceof SynthesizedAnnotation || - AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement) || - AnnotationFilter.PLAIN.matches(annotation)) { - return annotation; - } - return MergedAnnotation.from(annotatedElement, annotation).synthesize(); - } - /** * Clear the internal annotation metadata cache. * @since 4.3.15 @@ -1259,11 +1286,6 @@ public abstract class AnnotationUtils { AnnotationsScanner.clearCache(); } - private static boolean isSingleLevelPresent( - MergedAnnotation mergedAnnotation) { - int depth = mergedAnnotation.getDepth(); - return depth == 0 || depth == 1; - } /** * Internal holder used to wrap default values. @@ -1280,7 +1302,6 @@ public abstract class AnnotationUtils { public String toString() { return "*" + this.defaultValue; } - } } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java index 881f904f77..11ec3360ff 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java @@ -51,6 +51,7 @@ import org.springframework.lang.Nullable; * synthesized} back into an actual {@link java.lang.annotation.Annotation}. * * @author Phillip Webb + * @author Juergen Hoeller * @since 5.2 * @param the annotation type * @see MergedAnnotations @@ -65,10 +66,10 @@ public interface MergedAnnotation { /** - * Return the class name of the actual annotation type. + * Return the {@code Class} reference for the actual annotation type. * @return the annotation type */ - String getType(); + Class getType(); /** * Return if the annotation is present on the source. Considers @@ -323,8 +324,7 @@ public interface MergedAnnotation { * @return the value as a enum * @throws NoSuchElementException if there is no matching attribute */ - > E getEnum(String attributeName, Class type) - throws NoSuchElementException; + > E getEnum(String attributeName, Class type) throws NoSuchElementException; /** * Return a required enum array attribute value from the annotation. @@ -333,8 +333,7 @@ public interface MergedAnnotation { * @return the value as a enum array * @throws NoSuchElementException if there is no matching attribute */ - > E[] getEnumArray(String attributeName, Class type) - throws NoSuchElementException; + > E[] getEnumArray(String attributeName, Class type) throws NoSuchElementException; /** * Return a required annotation attribute value from the annotation. @@ -353,8 +352,8 @@ public interface MergedAnnotation { * @return the value as a {@link MergedAnnotation} array * @throws NoSuchElementException if there is no matching attribute */ - MergedAnnotation[] getAnnotationArray(String attributeName, - Class type) throws NoSuchElementException; + MergedAnnotation[] getAnnotationArray(String attributeName, Class type) + throws NoSuchElementException; /** * Return an optional attribute value from the annotation. @@ -422,24 +421,21 @@ public interface MergedAnnotation { MergedAnnotation withNonMergedAttributes(); /** - * Return an immutable {@link Map} that contains all the annotation - * attributes. The {@link MapValues} options may be used to change the way - * that values are added. + * Return an immutable {@link Map} that contains all the annotation attributes. + * The {@link MapValues} options may be used to change the way that values are added. * @param options map value options - * @return a map containing the attributes and values + * @return an immutable map containing the attributes and values */ Map asMap(MapValues... options); /** - * Return a {@link Map} of the supplied type that contains all the annotation - * attributes. The {@link MapValues} options may be used to change the way - * that values are added. - * @param factory a map factory or {@code null} to return an immutable map. + * Return a {@link Map} of the given type that contains all the annotation attributes. + * The {@link MapValues} options may be used to change the way that values are added. + * @param factory a map factory * @param options map value options * @return a map containing the attributes and values */ - > T asMap( - @Nullable Function, T> factory, MapValues... options); + > T asMap(Function, T> factory, MapValues... options); /** * Return a type-safe synthesized version of this annotation that can be @@ -460,8 +456,7 @@ public interface MergedAnnotation { * @throws NoSuchElementException on a missing annotation * @see MergedAnnotationPredicates */ - Optional synthesize(@Nullable Predicate> condition) - throws NoSuchElementException; + Optional synthesize(Predicate> condition) throws NoSuchElementException; /** @@ -511,10 +506,9 @@ public interface MergedAnnotation { * Create a new {@link MergedAnnotations} instance from the specified * annotation type and attributes maps. * @param annotationType the annotation type - * @param attributes the annotation attributes or {@code null} if just - * default values should be used - * @return a {@link MergedAnnotation} instance for the annotation and - * attributes + * @param attributes the annotation attributes or {@code null} if just default + * values should be used + * @return a {@link MergedAnnotation} instance for the annotation and attributes * @see #from(AnnotatedElement, Class, Map) */ static MergedAnnotation from( @@ -530,10 +524,9 @@ public interface MergedAnnotation { * information and logging. It does not need to actually contain * the specified annotations and it will not be searched. * @param annotationType the annotation type - * @param attributes the annotation attributes or {@code null} if just - * default values should be used - * @return a {@link MergedAnnotation} instance for the annotation and - * attributes + * @param attributes the annotation attributes or {@code null} if just default + * values should be used + * @return a {@link MergedAnnotation} instance for the annotation and attributes */ static MergedAnnotation from( @Nullable AnnotatedElement source, Class annotationType, @Nullable Map attributes) { diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationPredicates.java b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationPredicates.java index 83e1307268..6914f9f3fc 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationPredicates.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationPredicates.java @@ -17,7 +17,6 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -50,7 +49,7 @@ public abstract class MergedAnnotationPredicates { * @return a {@link Predicate} to test the annotation type */ public static Predicate> typeIn(String... typeNames) { - return annotation -> ObjectUtils.containsElement(typeNames, annotation.getType()); + return annotation -> ObjectUtils.containsElement(typeNames, annotation.getType().getName()); } /** @@ -62,7 +61,7 @@ public abstract class MergedAnnotationPredicates { * @return a {@link Predicate} to test the annotation type */ public static Predicate> 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 Predicate> typeIn(Collection types) { return annotation -> types.stream() .map(type -> type instanceof Class ? ((Class) type).getName() : type.toString()) - .anyMatch(typeName -> typeName.equals(annotation.getType())); + .anyMatch(typeName -> typeName.equals(annotation.getType().getName())); } /** @@ -141,7 +140,6 @@ public abstract class MergedAnnotationPredicates { return ObjectUtils.nullSafeEquals(value, this.lastValue); } - } @@ -165,7 +163,6 @@ public abstract class MergedAnnotationPredicates { K key = this.keyExtractor.apply(annotation); return this.seen.add(key); } - } } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java index eb4125071d..989f99f449 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java @@ -31,6 +31,7 @@ import org.springframework.lang.Nullable; * {@link MergedAnnotation#missing()}. * * @author Phillip Webb + * @author Juergen Hoeller * @since 5.2 * @param the annotation type */ @@ -44,7 +45,7 @@ final class MissingMergedAnnotation extends AbstractMerged @Override - public String getType() { + public Class getType() { throw new NoSuchElementException("Unable to get type for missing annotation"); } @@ -112,13 +113,8 @@ final class MissingMergedAnnotation extends AbstractMerged } @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) - public > T asMap( - @Nullable Function, T> factory, MapValues... options) { - if (factory != null) { - return factory.apply(this); - } - return (T) ((Map) Collections.emptyMap()); + public > T asMap(Function, T> factory, MapValues... options) { + return factory.apply(this); } @Override diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java index eab644e45c..6bd6caaa34 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java @@ -176,7 +176,7 @@ final class SynthesizedMergedAnnotationInvocationHandler i Class type = ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType()); return this.annotation.getValue(name, type).orElseThrow( () -> new NoSuchElementException("No value found for attribute named '" + name + - "' in merged annotation " + this.annotation.getType())); + "' in merged annotation " + this.annotation.getType().getName())); } @SuppressWarnings("unchecked") diff --git a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java index df3fe6583f..61f7765625 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java @@ -49,7 +49,7 @@ import org.springframework.util.ReflectionUtils; * return type, namely: * *

- * + * * * *
Return TypeExtracted Type
Return TypeExtracted Type
ClassClass or String
Class[]Class[] or String[]
AnnotationAnnotation, Map or Object compatible with the value @@ -60,6 +60,7 @@ import org.springframework.util.ReflectionUtils; *
* * @author Phillip Webb + * @author Juergen Hoeller * @since 5.2 * @param the annotation type * @see TypeMappedAnnotations @@ -91,17 +92,16 @@ final class TypeMappedAnnotation extends AbstractMergedAnn private String string; - private TypeMappedAnnotation(AnnotationTypeMapping mapping, - @Nullable Object source, @Nullable Object rootAttributes, - BiFunction valueExtractor, int aggregateIndex) { + private TypeMappedAnnotation(AnnotationTypeMapping mapping, @Nullable Object source, + @Nullable Object rootAttributes, BiFunction valueExtractor, + int aggregateIndex) { this(mapping, source, rootAttributes, valueExtractor, aggregateIndex, null); } - private TypeMappedAnnotation(AnnotationTypeMapping mapping, - @Nullable Object source, @Nullable Object rootAttributes, - BiFunction valueExtractor, int aggregateIndex, - @Nullable int[] resolvedRootMirrors) { + private TypeMappedAnnotation(AnnotationTypeMapping mapping, @Nullable Object source, + @Nullable Object rootAttributes, BiFunction valueExtractor, + int aggregateIndex, @Nullable int[] resolvedRootMirrors) { this.source = source; this.rootAttributes = rootAttributes; @@ -110,18 +110,15 @@ final class TypeMappedAnnotation extends AbstractMergedAnn this.aggregateIndex = aggregateIndex; this.useMergedValues = true; this.attributeFilter = null; - this.resolvedRootMirrors = resolvedRootMirrors != null ? resolvedRootMirrors - : mapping.getRoot().getMirrorSets().resolve(source, rootAttributes, - this.valueExtractor); - this.resolvedMirrors = getDepth() == 0 ? this.resolvedRootMirrors - : mapping.getMirrorSets().resolve(source, this, - this::getValueForMirrorResolution); + this.resolvedRootMirrors = (resolvedRootMirrors != null ? resolvedRootMirrors : + mapping.getRoot().getMirrorSets().resolve(source, rootAttributes, this.valueExtractor)); + this.resolvedMirrors = (getDepth() == 0 ? this.resolvedRootMirrors : + mapping.getMirrorSets().resolve(source, this, this::getValueForMirrorResolution)); } - private TypeMappedAnnotation(AnnotationTypeMapping mapping, - @Nullable Object source, @Nullable Object rootAnnotation, - BiFunction valueExtractor, int aggregateIndex, - boolean useMergedValues, @Nullable Predicate attributeFilter, + private TypeMappedAnnotation(AnnotationTypeMapping mapping, @Nullable Object source, + @Nullable Object rootAnnotation, BiFunction valueExtractor, + int aggregateIndex, boolean useMergedValues, @Nullable Predicate attributeFilter, int[] resolvedRootMirrors, int[] resolvedMirrors) { this.source = source; @@ -136,16 +133,10 @@ final class TypeMappedAnnotation extends AbstractMergedAnn } - @Nullable - private Object getValueForMirrorResolution(Method attribute, Object annotation) { - int attributeIndex = this.mapping.getAttributes().indexOf(attribute); - boolean valueAttribute = VALUE.equals(attribute.getName()); - return getValue(attributeIndex, !valueAttribute, true); - } - @Override - public String getType() { - return getAnnotationType().getName(); + @SuppressWarnings("unchecked") + public Class getType() { + return (Class) this.mapping.getAnnotationType(); } @Override @@ -184,8 +175,7 @@ final class TypeMappedAnnotation extends AbstractMergedAnn public boolean hasDefaultValue(String attributeName) { int attributeIndex = getAttributeIndex(attributeName, true); Object value = getValue(attributeIndex, true, false); - return value == null || this.mapping.isEquivalentToDefaultValue(attributeIndex, value, - this.valueExtractor); + return (value == null || this.mapping.isEquivalentToDefaultValue(attributeIndex, value, this.valueExtractor)); } @Override @@ -197,8 +187,8 @@ final class TypeMappedAnnotation extends AbstractMergedAnn Method attribute = this.mapping.getAttributes().get(attributeIndex); Assert.notNull(type, "Type must not be null"); Assert.isAssignable(type, attribute.getReturnType(), - "Attribute " + attributeName + " type mismatch:"); - return (MergedAnnotation) getRequiredValue(attributeIndex, Object.class); + () -> "Attribute " + attributeName + " type mismatch:"); + return (MergedAnnotation) getRequiredValue(attributeIndex); } @Override @@ -212,7 +202,7 @@ final class TypeMappedAnnotation extends AbstractMergedAnn Assert.notNull(type, "Type must not be null"); Assert.notNull(componentType, () -> "Attribute " + attributeName + " is not an array"); Assert.isAssignable(type, componentType, () -> "Attribute " + attributeName + " component type mismatch:"); - return (MergedAnnotation[]) getRequiredValue(attributeIndex, Object.class); + return (MergedAnnotation[]) getRequiredValue(attributeIndex); } @Override @@ -243,55 +233,48 @@ final class TypeMappedAnnotation extends AbstractMergedAnn } @Override - @SuppressWarnings("unchecked") - public > T asMap( - @Nullable Function, T> factory, MapValues... options) { + public Map asMap(MapValues... options) { + return Collections.unmodifiableMap(asMap(mergedAnnotation -> new LinkedHashMap<>(), options)); + } - T map = (factory != null ? factory.apply(this) : (T) new LinkedHashMap()); + @Override + public > T asMap(Function, T> factory, MapValues... options) { + T map = factory.apply(this); Assert.state(map != null, "Factory used to create MergedAnnotation Map must not return null"); AttributeMethods attributes = this.mapping.getAttributes(); for (int i = 0; i < attributes.size(); i++) { Method attribute = attributes.get(i); - Object value = isFiltered(attribute.getName()) ? - null : - getValue(i, getTypeForMapOptions(attribute, options)); + Object value = (isFiltered(attribute.getName()) ? null : + getValue(i, getTypeForMapOptions(attribute, options))); if (value != null) { map.put(attribute.getName(), - adaptValueForMapOptions(attribute, value, factory, options)); + adaptValueForMapOptions(attribute, value, map.getClass(), factory, options)); } } - return (factory != null) ? map : (T) Collections.unmodifiableMap(map); + return map; } private Class getTypeForMapOptions(Method attribute, MapValues[] options) { Class attributeType = attribute.getReturnType(); - Class componentType = attributeType.isArray() ? - attributeType.getComponentType() : - attributeType; + Class componentType = (attributeType.isArray() ? attributeType.getComponentType() : attributeType); if (MapValues.CLASS_TO_STRING.isIn(options) && componentType == Class.class) { - return attributeType.isArray() ? String[].class : String.class; + return (attributeType.isArray() ? String[].class : String.class); } return Object.class; } - private > Object adaptValueForMapOptions( - Method attribute, Object value, - @Nullable Function, T> factory, MapValues[] options) { + private > Object adaptValueForMapOptions(Method attribute, Object value, + Class mapType, Function, T> factory, MapValues[] options) { if (value instanceof MergedAnnotation) { MergedAnnotation annotation = (MergedAnnotation) value; - return MapValues.ANNOTATION_TO_MAP.isIn(options) ? - annotation.asMap(factory, options) : - annotation.synthesize(); + return (MapValues.ANNOTATION_TO_MAP.isIn(options) ? + annotation.asMap(factory, options) : annotation.synthesize()); } if (value instanceof MergedAnnotation[]) { MergedAnnotation[] annotations = (MergedAnnotation[]) value; if (MapValues.ANNOTATION_TO_MAP.isIn(options)) { - Class componentType = Map.class; - if (factory != null) { - componentType = factory.apply(this).getClass(); - } - Object result = Array.newInstance(componentType, annotations.length); + Object result = Array.newInstance(mapType, annotations.length); for (int i = 0; i < annotations.length; i++) { Array.set(result, i, annotations[i].asMap(factory, options)); } @@ -309,7 +292,7 @@ final class TypeMappedAnnotation extends AbstractMergedAnn @Override protected A createSynthesized() { - return SynthesizedMergedAnnotationInvocationHandler.createProxy(this, getAnnotationType()); + return SynthesizedMergedAnnotationInvocationHandler.createProxy(this, getType()); } @Override @@ -318,7 +301,7 @@ final class TypeMappedAnnotation extends AbstractMergedAnn if (string == null) { StringBuilder builder = new StringBuilder(); builder.append("@"); - builder.append(getType()); + builder.append(getType().getName()); builder.append("("); for (int i = 0; i < this.mapping.getAttributes().size(); i++) { Method attribute = this.mapping.getAttributes().get(i); @@ -360,8 +343,8 @@ final class TypeMappedAnnotation extends AbstractMergedAnn return (attributeIndex != -1 ? getValue(attributeIndex, type) : null); } - protected final T getRequiredValue(int attributeIndex, Class type) { - T value = getValue(attributeIndex, type); + private final Object getRequiredValue(int attributeIndex) { + Object value = getValue(attributeIndex, Object.class); if (value == null) { throw new NoSuchElementException("No element at attribute index " + attributeIndex); } @@ -379,9 +362,7 @@ final class TypeMappedAnnotation extends AbstractMergedAnn } @Nullable - private Object getValue(int attributeIndex, boolean useConventionMapping, - boolean forMirrorResolution) { - + private Object getValue(int attributeIndex, boolean useConventionMapping, boolean forMirrorResolution) { AnnotationTypeMapping mapping = this.mapping; if (this.useMergedValues) { int mappedIndex = this.mapping.getAliasMapping(attributeIndex); @@ -419,6 +400,13 @@ final class TypeMappedAnnotation extends AbstractMergedAnn return ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation()); } + @Nullable + private Object getValueForMirrorResolution(Method attribute, Object annotation) { + int attributeIndex = this.mapping.getAttributes().indexOf(attribute); + boolean valueAttribute = VALUE.equals(attribute.getName()); + return getValue(attributeIndex, !valueAttribute, true); + } + @SuppressWarnings("unchecked") @Nullable private T adapt(Method attribute, @Nullable Object value, Class type) { @@ -485,21 +473,16 @@ final class TypeMappedAnnotation extends AbstractMergedAnn } if (!attributeType.isInstance(value)) { throw new IllegalStateException("Attribute '" + attribute.getName() + - "' in annotation " + getType() + " should be compatible with " + + "' in annotation " + getType().getName() + " should be compatible with " + attributeType.getName() + " but a " + value.getClass().getName() + " value was returned"); } return value; } - private MergedAnnotation adaptToMergedAnnotation(Object value, - Class annotationType) { - - AnnotationFilter filter = AnnotationFilter.mostAppropriateFor(annotationType); - AnnotationTypeMapping mapping = AnnotationTypeMappings.forAnnotationType( - annotationType, filter).get(0); - return new TypeMappedAnnotation<>(mapping, this.source, value, - this.getValueExtractor(value), this.aggregateIndex); + private MergedAnnotation adaptToMergedAnnotation(Object value, Class annotationType) { + AnnotationTypeMapping mapping = AnnotationTypeMappings.forAnnotationType(annotationType).get(0); + return new TypeMappedAnnotation<>(mapping, this.source, value, getValueExtractor(value), this.aggregateIndex); } private BiFunction getValueExtractor(Object value) { @@ -529,7 +512,7 @@ final class TypeMappedAnnotation extends AbstractMergedAnn this.mapping.getAttributes().indexOf(attributeName)); if (attributeIndex == -1 && required) { throw new NoSuchElementException("No attribute named '" + attributeName + - "' present in merged annotation " + getType()); + "' present in merged annotation " + getType().getName()); } return attributeIndex; } @@ -541,10 +524,6 @@ final class TypeMappedAnnotation extends AbstractMergedAnn return false; } - @SuppressWarnings("unchecked") - private Class getAnnotationType() { - return (Class) this.mapping.getAnnotationType(); - } static MergedAnnotation from(@Nullable Object source, A annotation) { Assert.notNull(annotation, "Annotation must not be null"); @@ -557,8 +536,8 @@ final class TypeMappedAnnotation extends AbstractMergedAnn Assert.notNull(annotationType, "Annotation type must not be null"); AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotationType); - return new TypeMappedAnnotation<>(mappings.get(0), source, attributes, - TypeMappedAnnotation::extractFromMap, 0); + return new TypeMappedAnnotation<>( + mappings.get(0), source, attributes, TypeMappedAnnotation::extractFromMap, 0); } @Nullable diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java index 4aaf682b36..00bb15ed6b 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java @@ -23,7 +23,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.Date; import java.util.List; import java.util.Set; import javax.annotation.Resource; @@ -34,9 +36,9 @@ import org.junit.Test; import org.junit.internal.ArrayComparisonFailure; import org.junit.rules.ExpectedException; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.stereotype.Indexed; -import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import static java.util.Arrays.*; @@ -631,7 +633,6 @@ public class AnnotatedElementUtilsTests { } private AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, Class annotationType) { - Assert.notNull(annotationType, "annotationType must not be null"); return AnnotatedElementUtils.findMergedAnnotationAttributes(element, annotationType.getName(), false, false); } @@ -699,6 +700,15 @@ public class AnnotatedElementUtilsTests { assertArrayEquals("path attribute: ", asArray("/test"), webMapping.path()); } + @Test + public void javaLangAnnotationTypeViaFindMergedAnnotation() throws Exception { + Constructor deprecatedCtor = Date.class.getConstructor(String.class); + assertEquals(deprecatedCtor.getAnnotation(Deprecated.class), + findMergedAnnotation(deprecatedCtor, Deprecated.class)); + assertEquals(Date.class.getAnnotation(Deprecated.class), + findMergedAnnotation(Date.class, Deprecated.class)); + } + @Test public void javaxAnnotationTypeViaFindMergedAnnotation() throws Exception { assertEquals(ResourceHolder.class.getAnnotation(Resource.class), @@ -707,17 +717,26 @@ public class AnnotatedElementUtilsTests { findMergedAnnotation(SpringAppConfigClass.class, Resource.class)); } + @Test + public void nullableAnnotationTypeViaFindMergedAnnotation() throws Exception { + Method method = TransactionalServiceImpl.class.getMethod("doIt"); + assertEquals(method.getAnnotation(Resource.class), + findMergedAnnotation(method, Resource.class)); + assertEquals(method.getAnnotation(Resource.class), + findMergedAnnotation(method, Resource.class)); + } + @Test public void getAllMergedAnnotationsOnClassWithInterface() throws Exception { - Method m = TransactionalServiceImpl.class.getMethod("doIt"); - Set allMergedAnnotations = getAllMergedAnnotations(m, Transactional.class); + Method method = TransactionalServiceImpl.class.getMethod("doIt"); + Set allMergedAnnotations = getAllMergedAnnotations(method, Transactional.class); assertTrue(allMergedAnnotations.isEmpty()); } @Test public void findAllMergedAnnotationsOnClassWithInterface() throws Exception { - Method m = TransactionalServiceImpl.class.getMethod("doIt"); - Set allMergedAnnotations = findAllMergedAnnotations(m, Transactional.class); + Method method = TransactionalServiceImpl.class.getMethod("doIt"); + Set allMergedAnnotations = findAllMergedAnnotations(method, Transactional.class); assertEquals(1, allMergedAnnotations.size()); } @@ -1299,20 +1318,22 @@ public class AnnotatedElementUtilsTests { interface TransactionalService { @Transactional - void doIt(); + @Nullable + Object doIt(); } class TransactionalServiceImpl implements TransactionalService { @Override - public void doIt() { + @Nullable + public Object doIt() { + return null; } } @Deprecated @ComponentScan class ForAnnotationsClass { - } } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationFilterTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationFilterTests.java index bca5ce92ab..2f2d0aaa47 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationFilterTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationFilterTests.java @@ -99,39 +99,6 @@ public class AnnotationFilterTests { assertThat(AnnotationFilter.NONE.matches(TestAnnotation.class)).isFalse(); } - @Test - public void pacakgesReturnsPackagesAnnotationFilter() { - assertThat(AnnotationFilter.packages("com.example")).isInstanceOf(PackagesAnnotationFilter.class); - } - - @Test - public void mostAppropriateForCollectionReturnsPlainWhenPossible() { - AnnotationFilter filter = AnnotationFilter.mostAppropriateFor( - Arrays.asList(TestAnnotation.class, OtherAnnotation.class)); - assertThat(filter).isSameAs(AnnotationFilter.PLAIN); - } - - @Test - public void mostAppropriateForCollectionWhenCantUsePlainReturnsNone() { - AnnotationFilter filter = AnnotationFilter.mostAppropriateFor(Arrays.asList( - TestAnnotation.class, OtherAnnotation.class, Nullable.class)); - assertThat(filter).isSameAs(AnnotationFilter.NONE); - } - - @Test - public void mostAppropriateForArrayReturnsPlainWhenPossible() { - AnnotationFilter filter = AnnotationFilter.mostAppropriateFor( - TestAnnotation.class, OtherAnnotation.class); - assertThat(filter).isSameAs(AnnotationFilter.PLAIN); - } - - @Test - public void mostAppropriateForArrayWhenCantUsePlainReturnsNone() { - AnnotationFilter filter = AnnotationFilter.mostAppropriateFor( - TestAnnotation.class, OtherAnnotation.class, Nullable.class); - assertThat(filter).isSameAs(AnnotationFilter.NONE); - } - @Retention(RetentionPolicy.RUNTIME) @interface TestAnnotation { diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java index 1923a4a5c0..31be5ccec9 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java @@ -17,6 +17,7 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; @@ -36,7 +37,7 @@ import org.junit.rules.ExpectedException; import org.springframework.core.Ordered; import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass; -import org.springframework.lang.Nullable; +import org.springframework.lang.NonNullApi; import org.springframework.stereotype.Component; import static java.util.Arrays.*; @@ -55,6 +56,7 @@ import static org.springframework.core.annotation.AnnotationUtils.*; * @author Phillip Webb * @author Oleg Zhurakousky */ +@SuppressWarnings("deprecation") public class AnnotationUtilsTests { @Rule @@ -396,7 +398,7 @@ public class AnnotationUtilsTests { } @Test - public void isAnnotationDeclaredLocallyForAllScenarios() throws Exception { + public void isAnnotationDeclaredLocallyForAllScenarios() { // no class-level annotation assertFalse(isAnnotationDeclaredLocally(Transactional.class, NonAnnotatedInterface.class)); assertFalse(isAnnotationDeclaredLocally(Transactional.class, NonAnnotatedClass.class)); @@ -435,6 +437,12 @@ public class AnnotationUtilsTests { assertFalse(isAnnotationInherited(Order.class, SubNonInheritedAnnotationClass.class)); } + @Test + public void isAnnotationMetaPresentForJavaLangType() { + assertTrue(isAnnotationMetaPresent(Order.class, Documented.class)); + assertTrue(isAnnotationMetaPresent(NonNullApi.class, Documented.class)); + } + @Test public void getAnnotationAttributesWithoutAttributeAliases() { Component component = WebController.class.getAnnotation(Component.class); @@ -540,6 +548,13 @@ public class AnnotationUtilsTests { assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class)); } + @Test + public void findRepeatableAnnotation() { + Repeatable repeatable = findAnnotation(MyRepeatable.class, Repeatable.class); + assertNotNull(repeatable); + assertEquals(MyRepeatableContainer.class, repeatable.value()); + } + @Test public void getRepeatableAnnotationsDeclaredOnMethod() throws Exception { Method method = InterfaceWithRepeated.class.getMethod("foo"); @@ -924,10 +939,8 @@ public class AnnotationUtilsTests { exception.expect(IllegalArgumentException.class); exception.expectMessage(either(allOf(startsWith("Attributes map"), containsString("returned null for required attribute 'text'"), - containsString("defined by annotation type [" - + AnnotationWithoutDefaults.class.getName() + "]"))).or( - containsString( - "No value found for attribute named 'text' in merged annotation"))); + containsString("defined by annotation type [" + AnnotationWithoutDefaults.class.getName() + "]"))).or( + containsString("No value found for attribute named 'text' in merged annotation"))); synthesizeAnnotation(attributes, AnnotationWithoutDefaults.class, null); } @@ -936,8 +949,8 @@ public class AnnotationUtilsTests { Map map = Collections.singletonMap(VALUE, 42L); exception.expect(IllegalArgumentException.class); exception.expectMessage(containsString( - "Attribute 'value' in annotation org.springframework.stereotype.Component " - + "should be compatible with java.lang.String but a java.lang.Long value was returned")); + "Attribute 'value' in annotation org.springframework.stereotype.Component " + + "should be compatible with java.lang.String but a java.lang.Long value was returned")); synthesizeAnnotation(map, Component.class, null); } @@ -1055,12 +1068,6 @@ public class AnnotationUtilsTests { void fromInterfaceImplementedByRoot(); } - public interface NullableAnnotatedInterface { - - @Nullable - void fromInterfaceImplementedByRoot(); - } - public static class Root implements AnnotatedInterface { @Order(27) diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index e767c15dc3..d6b0a8b44d 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -77,18 +77,17 @@ public class MergedAnnotationsTests { @Test public void streamWhenFromClassWithMetaDepth1() { - Stream names = MergedAnnotations.from(TransactionalComponent.class) + Stream> classes = MergedAnnotations.from(TransactionalComponent.class) .stream().map(MergedAnnotation::getType); - assertThat(names).containsExactly(Transactional.class.getName(), - Component.class.getName(), Indexed.class.getName()); + assertThat(classes).containsExactly(Transactional.class, Component.class, Indexed.class); } @Test public void streamWhenFromClassWithMetaDepth2() { - Stream names = MergedAnnotations.from(ComposedTransactionalComponent.class) + Stream> classes = MergedAnnotations.from(ComposedTransactionalComponent.class) .stream().map(MergedAnnotation::getType); - assertThat(names).containsExactly(TransactionalComponent.class.getName(), - Transactional.class.getName(), Component.class.getName(), Indexed.class.getName()); + assertThat(classes).containsExactly(TransactionalComponent.class, + Transactional.class, Component.class, Indexed.class); } @Test @@ -1149,17 +1148,16 @@ public class MergedAnnotationsTests { @Test public void getDirectWithoutAttributeAliases() { - MergedAnnotation annotation = MergedAnnotations.from(WebController.class).get( - Component.class); + MergedAnnotation annotation = MergedAnnotations.from(WebController.class) + .get(Component.class); assertThat(annotation.getString("value")).isEqualTo("webController"); } @Test public void getDirectWithNestedAnnotations() { - MergedAnnotation annotation = MergedAnnotations.from( - ComponentScanClass.class).get(ComponentScan.class); - MergedAnnotation[] filters = annotation.getAnnotationArray( - "excludeFilters", Filter.class); + MergedAnnotation annotation = MergedAnnotations.from(ComponentScanClass.class) + .get(ComponentScan.class); + MergedAnnotation[] filters = annotation.getAnnotationArray("excludeFilters", Filter.class); assertThat(Arrays.stream(filters).map( filter -> filter.getString("pattern"))).containsExactly("*Foo", "*Bar"); } @@ -1167,8 +1165,7 @@ public class MergedAnnotationsTests { @Test public void getDirectWithAttributeAliases1() throws Exception { Method method = WebController.class.getMethod("handleMappedWithValueAttribute"); - MergedAnnotation annotation = MergedAnnotations.from(method).get( - RequestMapping.class); + MergedAnnotation annotation = MergedAnnotations.from(method).get(RequestMapping.class); assertThat(annotation.getString("name")).isEqualTo("foo"); assertThat(annotation.getStringArray("value")).containsExactly("/test"); assertThat(annotation.getStringArray("path")).containsExactly("/test"); @@ -1177,8 +1174,7 @@ public class MergedAnnotationsTests { @Test public void getDirectWithAttributeAliases2() throws Exception { Method method = WebController.class.getMethod("handleMappedWithPathAttribute"); - MergedAnnotation annotation = MergedAnnotations.from(method).get( - RequestMapping.class); + MergedAnnotation annotation = MergedAnnotations.from(method).get(RequestMapping.class); assertThat(annotation.getString("name")).isEqualTo("bar"); assertThat(annotation.getStringArray("value")).containsExactly("/test"); assertThat(annotation.getStringArray("path")).containsExactly("/test"); @@ -1186,8 +1182,7 @@ public class MergedAnnotationsTests { @Test public void getDirectWithAttributeAliasesWithDifferentValues() throws Exception { - Method method = WebController.class.getMethod( - "handleMappedWithDifferentPathAndValueAttributes"); + Method method = WebController.class.getMethod("handleMappedWithDifferentPathAndValueAttributes"); assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( () -> MergedAnnotations.from(method).get( RequestMapping.class)).withMessageContaining( @@ -1197,10 +1192,9 @@ public class MergedAnnotationsTests { @Test public void getValueFromAnnotation() throws Exception { - Method method = TransactionalStringGeneric.class.getMethod("something", - Object.class); - MergedAnnotation annotation = MergedAnnotations.from(method, - SearchStrategy.EXHAUSTIVE).get(Order.class); + Method method = TransactionalStringGeneric.class.getMethod("something", Object.class); + MergedAnnotation annotation = MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE) + .get(Order.class); assertThat(annotation.getInt("value")).isEqualTo(1); } @@ -1210,21 +1204,17 @@ public class MergedAnnotationsTests { assertThat(declaredAnnotations).hasSize(1); Annotation annotation = declaredAnnotations[0]; MergedAnnotation mergedAnnotation = MergedAnnotation.from(annotation); - assertThat(mergedAnnotation.getType()).contains("NonPublicAnnotation"); - assertThat( - mergedAnnotation.synthesize().annotationType().getSimpleName()).isEqualTo( - "NonPublicAnnotation"); + assertThat(mergedAnnotation.getType().getSimpleName()).isEqualTo("NonPublicAnnotation"); + assertThat(mergedAnnotation.synthesize().annotationType().getSimpleName()).isEqualTo("NonPublicAnnotation"); assertThat(mergedAnnotation.getInt("value")).isEqualTo(42); } @Test public void getDefaultValueFromAnnotation() throws Exception { - Method method = TransactionalStringGeneric.class.getMethod("something", - Object.class); - MergedAnnotation annotation = MergedAnnotations.from(method, - SearchStrategy.EXHAUSTIVE).get(Order.class); - assertThat(annotation.getDefaultValue("value")).contains( - Ordered.LOWEST_PRECEDENCE); + Method method = TransactionalStringGeneric.class.getMethod("something", Object.class); + MergedAnnotation annotation = MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE) + .get(Order.class); + assertThat(annotation.getDefaultValue("value")).contains(Ordered.LOWEST_PRECEDENCE); } @Test @@ -1233,7 +1223,7 @@ public class MergedAnnotationsTests { assertThat(declaredAnnotations).hasSize(1); Annotation declaredAnnotation = declaredAnnotations[0]; MergedAnnotation annotation = MergedAnnotation.from(declaredAnnotation); - assertThat(annotation.getType()).isEqualTo( + assertThat(annotation.getType().getName()).isEqualTo( "org.springframework.core.annotation.subpackage.NonPublicAnnotation"); assertThat(annotation.getDefaultValue("value")).contains(-1); } diff --git a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java index fc687537da..8734853322 100644 --- a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java +++ b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,7 +78,6 @@ public abstract class MetaAnnotationUtils { * @param annotationType the type of annotation to look for * @return the corresponding annotation descriptor if the annotation was found; * otherwise {@code null} - * @see AnnotationUtils#findAnnotationDeclaringClass(Class, Class) * @see #findAnnotationDescriptorForTypes(Class, Class...) */ @Nullable @@ -164,7 +163,6 @@ public abstract class MetaAnnotationUtils { * @param annotationTypes the types of annotations to look for * @return the corresponding annotation descriptor if one of the annotations * was found; otherwise {@code null} - * @see AnnotationUtils#findAnnotationDeclaringClassForTypes(java.util.List, Class) * @see #findAnnotationDescriptor(Class, Class) */ @SuppressWarnings("unchecked")