From 50e980c02f0dba610303e064289be0420d1f6ce1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 12 Mar 2018 13:40:26 +0100 Subject: [PATCH] Validate declared annotations before deciding between reflection and ASM Issue: SPR-16564 --- .../annotation/ConfigurationClassParser.java | 9 +++-- .../annotation/AnnotatedElementUtils.java | 36 +++++++++---------- .../core/annotation/AnnotationUtils.java | 29 +++++++++++++-- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 046889c47e..2e3800e14f 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -18,6 +18,7 @@ package org.springframework.context.annotation; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.annotation.Annotation; import java.net.UnknownHostException; import java.util.ArrayDeque; import java.util.ArrayList; @@ -55,6 +56,7 @@ import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; @@ -659,8 +661,11 @@ class ConfigurationClassParser { return new SourceClass(Object.class); } try { - // Sanity test that we can read annotations, if not fall back to ASM - classType.getAnnotations(); + // Sanity test that we can reflectively read annotations, + // including Class attributes; if not -> fall back to ASM + for (Annotation ann : classType.getAnnotations()) { + AnnotationUtils.validateAnnotation(ann); + } return new SourceClass(classType); } catch (Throwable ex) { 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 63a769b4f8..0bd78ec559 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 @@ -60,10 +60,9 @@ import org.springframework.util.MultiValueMap; * individual method for details on which search algorithm is used. * *

Get semantics are limited to searching for annotations - * that are either present on an {@code AnnotatedElement} (i.e., - * declared locally or {@linkplain java.lang.annotation.Inherited inherited}) - * or declared within the annotation hierarchy above the - * {@code AnnotatedElement}. + * that are either present on an {@code AnnotatedElement} (i.e. declared + * locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared + * within the annotation hierarchy above the {@code AnnotatedElement}. * *

Find semantics are much more exhaustive, providing * get semantics plus support for the following: @@ -77,14 +76,13 @@ import org.springframework.util.MultiValueMap; * * *

Support for {@code @Inherited}

- *

Methods following get semantics will honor the contract of - * Java's {@link java.lang.annotation.Inherited @Inherited} annotation except - * that locally declared annotations (including custom composed annotations) - * will be favored over inherited annotations. In contrast, methods following - * find semantics will completely ignore the presence of - * {@code @Inherited} since the find search algorithm manually - * traverses type and method hierarchies and thereby implicitly supports - * annotation inheritance without the need for {@code @Inherited}. + *

Methods following get semantics will honor the contract of Java's + * {@link java.lang.annotation.Inherited @Inherited} annotation except that locally + * declared annotations (including custom composed annotations) will be favored over + * inherited annotations. In contrast, methods following find semantics + * will completely ignore the presence of {@code @Inherited} since the find + * search algorithm manually traverses type and method hierarchies and thereby + * implicitly supports annotation inheritance without a need for {@code @Inherited}. * * @author Phillip Webb * @author Juergen Hoeller @@ -794,7 +792,7 @@ public class AnnotatedElementUtils { * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) */ @Nullable private static T searchWithGetSemantics(AnnotatedElement element, @@ -815,7 +813,7 @@ public class AnnotatedElementUtils { * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @param processor the processor to delegate to - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.3 */ @Nullable @@ -848,7 +846,7 @@ public class AnnotatedElementUtils { * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) */ @Nullable private static T searchWithGetSemantics(AnnotatedElement element, @@ -909,7 +907,7 @@ public class AnnotatedElementUtils { * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.2 */ @Nullable @@ -979,7 +977,7 @@ public class AnnotatedElementUtils { * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.2 */ @Nullable @@ -1001,7 +999,7 @@ public class AnnotatedElementUtils { * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @param processor the processor to delegate to - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.3 */ @Nullable @@ -1039,7 +1037,7 @@ public class AnnotatedElementUtils { * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.2 */ @Nullable 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 f764ceddbf..3fb11e3095 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 @@ -898,6 +898,31 @@ public abstract class AnnotationUtils { return (annotationType != null && annotationType.startsWith("java.lang.annotation")); } + /** + * Check the declared attributes of the given annotation, in particular covering + * Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for + * {@code Class} values (instead of early {@code Class.getAnnotations() failure}. + *

This method not failing indicates that {@link #getAnnotationAttributes(Annotation)} + * won't failure either (when attempted later on). + * @param annotation the annotation to validate + * @throws IllegalStateException if a declared {@code Class} attribute could not be read + * @since 4.3.15 + * @see Class#getAnnotations() + * @see #getAnnotationAttributes(Annotation) + */ + public static void validateAnnotation(Annotation annotation) { + for (Method method : getAttributeMethods(annotation.annotationType())) { + if (method.getReturnType() == Class.class) { + try { + method.invoke(annotation); + } + catch (Throwable ex) { + throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex); + } + } + } + } + /** * Retrieve the given annotation's attributes as a {@link Map}, preserving all * attribute types. @@ -1835,13 +1860,13 @@ public abstract class AnnotationUtils { if (element instanceof Class && Annotation.class.isAssignableFrom((Class) element)) { // Meta-annotation or (default) value lookup on an annotation type if (loggerToUse.isDebugEnabled()) { - loggerToUse.debug("Failed to meta-introspect annotation [" + element + "]: " + ex); + loggerToUse.debug("Failed to meta-introspect annotation " + element + ": " + ex); } } else { // Direct annotation lookup on regular Class, Method, Field if (loggerToUse.isInfoEnabled()) { - loggerToUse.info("Failed to introspect annotations on [" + element + "]: " + ex); + loggerToUse.info("Failed to introspect annotations on " + element + ": " + ex); } } } -- GitLab