From 7cafa6764ce12191bd824da06b88729dffc20e69 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 28 Mar 2018 01:20:59 +0200 Subject: [PATCH] Validate declared annotations before deciding between reflection and ASM Issue: SPR-16564 --- .../annotation/ConfigurationClassParser.java | 11 ++++-- .../annotation/AnnotatedElementUtils.java | 38 +++++++++---------- .../core/annotation/AnnotationUtils.java | 32 ++++++++++++++-- 3 files changed, 55 insertions(+), 26 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 d10fb2bb29..b6af650b19 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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; @@ -53,6 +54,7 @@ import org.springframework.context.annotation.ConfigurationCondition.Configurati import org.springframework.core.NestedIOException; 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; @@ -656,8 +658,11 @@ class ConfigurationClassParser { */ SourceClass asSourceClass(Class classType) throws IOException { 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 d83747fee8..01fec3f0d4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,10 +59,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: @@ -76,14 +75,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 @@ -873,7 +871,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}) */ private static T searchWithGetSemantics(AnnotatedElement element, Class annotationType, String annotationName, Processor processor) { @@ -892,7 +890,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 */ private static T searchWithGetSemantics(AnnotatedElement element, Class annotationType, @@ -923,7 +921,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}) */ private static T searchWithGetSemantics(AnnotatedElement element, Class annotationType, String annotationName, Class containerType, Processor processor, @@ -984,7 +982,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 */ private static T searchWithGetSemanticsInAnnotations(AnnotatedElement element, @@ -1053,7 +1051,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 */ private static T searchWithFindSemantics(AnnotatedElement element, Class annotationType, @@ -1073,7 +1071,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 */ private static T searchWithFindSemantics(AnnotatedElement element, Class annotationType, @@ -1109,7 +1107,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 */ private static T searchWithFindSemantics(AnnotatedElement element, Class 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 5e19db4649..ed091d85d6 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -932,6 +932,32 @@ 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())) { + Class returnType = method.getReturnType(); + if (returnType == Class.class || returnType == 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. @@ -1882,13 +1908,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); } } }