Skip to content

Commit 50e980c

Browse files
committed
Validate declared annotations before deciding between reflection and ASM
Issue: SPR-16564
1 parent 1b1a69a commit 50e980c

File tree

3 files changed

+51
-23
lines changed

3 files changed

+51
-23
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.FileNotFoundException;
2020
import java.io.IOException;
21+
import java.lang.annotation.Annotation;
2122
import java.net.UnknownHostException;
2223
import java.util.ArrayDeque;
2324
import java.util.ArrayList;
@@ -55,6 +56,7 @@
5556
import org.springframework.core.Ordered;
5657
import org.springframework.core.annotation.AnnotationAttributes;
5758
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
59+
import org.springframework.core.annotation.AnnotationUtils;
5860
import org.springframework.core.env.CompositePropertySource;
5961
import org.springframework.core.env.ConfigurableEnvironment;
6062
import org.springframework.core.env.Environment;
@@ -659,8 +661,11 @@ SourceClass asSourceClass(@Nullable Class<?> classType) throws IOException {
659661
return new SourceClass(Object.class);
660662
}
661663
try {
662-
// Sanity test that we can read annotations, if not fall back to ASM
663-
classType.getAnnotations();
664+
// Sanity test that we can reflectively read annotations,
665+
// including Class attributes; if not -> fall back to ASM
666+
for (Annotation ann : classType.getAnnotations()) {
667+
AnnotationUtils.validateAnnotation(ann);
668+
}
664669
return new SourceClass(classType);
665670
}
666671
catch (Throwable ex) {

spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,9 @@
6060
* individual method for details on which search algorithm is used.
6161
*
6262
* <p><strong>Get semantics</strong> are limited to searching for annotations
63-
* that are either <em>present</em> on an {@code AnnotatedElement} (i.e.,
64-
* declared locally or {@linkplain java.lang.annotation.Inherited inherited})
65-
* or declared within the annotation hierarchy <em>above</em> the
66-
* {@code AnnotatedElement}.
63+
* that are either <em>present</em> on an {@code AnnotatedElement} (i.e. declared
64+
* locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared
65+
* within the annotation hierarchy <em>above</em> the {@code AnnotatedElement}.
6766
*
6867
* <p><strong>Find semantics</strong> are much more exhaustive, providing
6968
* <em>get semantics</em> plus support for the following:
@@ -77,14 +76,13 @@
7776
* </ul>
7877
*
7978
* <h3>Support for {@code @Inherited}</h3>
80-
* <p>Methods following <em>get semantics</em> will honor the contract of
81-
* Java's {@link java.lang.annotation.Inherited @Inherited} annotation except
82-
* that locally declared annotations (including custom composed annotations)
83-
* will be favored over inherited annotations. In contrast, methods following
84-
* <em>find semantics</em> will completely ignore the presence of
85-
* {@code @Inherited} since the <em>find</em> search algorithm manually
86-
* traverses type and method hierarchies and thereby implicitly supports
87-
* annotation inheritance without the need for {@code @Inherited}.
79+
* <p>Methods following <em>get semantics</em> will honor the contract of Java's
80+
* {@link java.lang.annotation.Inherited @Inherited} annotation except that locally
81+
* declared annotations (including custom composed annotations) will be favored over
82+
* inherited annotations. In contrast, methods following <em>find semantics</em>
83+
* will completely ignore the presence of {@code @Inherited} since the <em>find</em>
84+
* search algorithm manually traverses type and method hierarchies and thereby
85+
* implicitly supports annotation inheritance without a need for {@code @Inherited}.
8886
*
8987
* @author Phillip Webb
9088
* @author Juergen Hoeller
@@ -794,7 +792,7 @@ public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(Anno
794792
* @param annotationName the fully qualified class name of the annotation
795793
* type to find (as an alternative to {@code annotationType})
796794
* @param processor the processor to delegate to
797-
* @return the result of the processor, potentially {@code null}
795+
* @return the result of the processor (potentially {@code null})
798796
*/
799797
@Nullable
800798
private static <T> T searchWithGetSemantics(AnnotatedElement element,
@@ -815,7 +813,7 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element,
815813
* @param containerType the type of the container that holds repeatable
816814
* annotations, or {@code null} if the annotation is not repeatable
817815
* @param processor the processor to delegate to
818-
* @return the result of the processor, potentially {@code null}
816+
* @return the result of the processor (potentially {@code null})
819817
* @since 4.3
820818
*/
821819
@Nullable
@@ -848,7 +846,7 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element,
848846
* @param processor the processor to delegate to
849847
* @param visited the set of annotated elements that have already been visited
850848
* @param metaDepth the meta-depth of the annotation
851-
* @return the result of the processor, potentially {@code null}
849+
* @return the result of the processor (potentially {@code null})
852850
*/
853851
@Nullable
854852
private static <T> T searchWithGetSemantics(AnnotatedElement element,
@@ -909,7 +907,7 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element,
909907
* @param processor the processor to delegate to
910908
* @param visited the set of annotated elements that have already been visited
911909
* @param metaDepth the meta-depth of the annotation
912-
* @return the result of the processor, potentially {@code null}
910+
* @return the result of the processor (potentially {@code null})
913911
* @since 4.2
914912
*/
915913
@Nullable
@@ -979,7 +977,7 @@ else if (currentAnnotationType == containerType) {
979977
* @param annotationName the fully qualified class name of the annotation
980978
* type to find (as an alternative to {@code annotationType})
981979
* @param processor the processor to delegate to
982-
* @return the result of the processor, potentially {@code null}
980+
* @return the result of the processor (potentially {@code null})
983981
* @since 4.2
984982
*/
985983
@Nullable
@@ -1001,7 +999,7 @@ private static <T> T searchWithFindSemantics(AnnotatedElement element,
1001999
* @param containerType the type of the container that holds repeatable
10021000
* annotations, or {@code null} if the annotation is not repeatable
10031001
* @param processor the processor to delegate to
1004-
* @return the result of the processor, potentially {@code null}
1002+
* @return the result of the processor (potentially {@code null})
10051003
* @since 4.3
10061004
*/
10071005
@Nullable
@@ -1039,7 +1037,7 @@ private static <T> T searchWithFindSemantics(AnnotatedElement element,
10391037
* @param processor the processor to delegate to
10401038
* @param visited the set of annotated elements that have already been visited
10411039
* @param metaDepth the meta-depth of the annotation
1042-
* @return the result of the processor, potentially {@code null}
1040+
* @return the result of the processor (potentially {@code null})
10431041
* @since 4.2
10441042
*/
10451043
@Nullable

spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,31 @@ public static boolean isInJavaLangAnnotationPackage(@Nullable String annotationT
898898
return (annotationType != null && annotationType.startsWith("java.lang.annotation"));
899899
}
900900

901+
/**
902+
* Check the declared attributes of the given annotation, in particular covering
903+
* Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for
904+
* {@code Class} values (instead of early {@code Class.getAnnotations() failure}.
905+
* <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)}
906+
* won't failure either (when attempted later on).
907+
* @param annotation the annotation to validate
908+
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
909+
* @since 4.3.15
910+
* @see Class#getAnnotations()
911+
* @see #getAnnotationAttributes(Annotation)
912+
*/
913+
public static void validateAnnotation(Annotation annotation) {
914+
for (Method method : getAttributeMethods(annotation.annotationType())) {
915+
if (method.getReturnType() == Class.class) {
916+
try {
917+
method.invoke(annotation);
918+
}
919+
catch (Throwable ex) {
920+
throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex);
921+
}
922+
}
923+
}
924+
}
925+
901926
/**
902927
* Retrieve the given annotation's attributes as a {@link Map}, preserving all
903928
* attribute types.
@@ -1835,13 +1860,13 @@ static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throw
18351860
if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) {
18361861
// Meta-annotation or (default) value lookup on an annotation type
18371862
if (loggerToUse.isDebugEnabled()) {
1838-
loggerToUse.debug("Failed to meta-introspect annotation [" + element + "]: " + ex);
1863+
loggerToUse.debug("Failed to meta-introspect annotation " + element + ": " + ex);
18391864
}
18401865
}
18411866
else {
18421867
// Direct annotation lookup on regular Class, Method, Field
18431868
if (loggerToUse.isInfoEnabled()) {
1844-
loggerToUse.info("Failed to introspect annotations on [" + element + "]: " + ex);
1869+
loggerToUse.info("Failed to introspect annotations on " + element + ": " + ex);
18451870
}
18461871
}
18471872
}

0 commit comments

Comments
 (0)