Skip to content

Commit 7cafa67

Browse files
committed
Validate declared annotations before deciding between reflection and ASM
Issue: SPR-16564
1 parent c1cb031 commit 7cafa67

File tree

3 files changed

+55
-26
lines changed

3 files changed

+55
-26
lines changed

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

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -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;
@@ -53,6 +54,7 @@
5354
import org.springframework.core.NestedIOException;
5455
import org.springframework.core.annotation.AnnotationAttributes;
5556
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
57+
import org.springframework.core.annotation.AnnotationUtils;
5658
import org.springframework.core.env.CompositePropertySource;
5759
import org.springframework.core.env.ConfigurableEnvironment;
5860
import org.springframework.core.env.Environment;
@@ -656,8 +658,11 @@ private SourceClass asSourceClass(ConfigurationClass configurationClass) throws
656658
*/
657659
SourceClass asSourceClass(Class<?> classType) throws IOException {
658660
try {
659-
// Sanity test that we can read annotations, if not fall back to ASM
660-
classType.getAnnotations();
661+
// Sanity test that we can reflectively read annotations,
662+
// including Class attributes; if not -> fall back to ASM
663+
for (Annotation ann : classType.getAnnotations()) {
664+
AnnotationUtils.validateAnnotation(ann);
665+
}
661666
return new SourceClass(classType);
662667
}
663668
catch (Throwable ex) {

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

+18-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -59,10 +59,9 @@
5959
* individual method for details on which search algorithm is used.
6060
*
6161
* <p><strong>Get semantics</strong> are limited to searching for annotations
62-
* that are either <em>present</em> on an {@code AnnotatedElement} (i.e.,
63-
* declared locally or {@linkplain java.lang.annotation.Inherited inherited})
64-
* or declared within the annotation hierarchy <em>above</em> the
65-
* {@code AnnotatedElement}.
62+
* that are either <em>present</em> on an {@code AnnotatedElement} (i.e. declared
63+
* locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared
64+
* within the annotation hierarchy <em>above</em> the {@code AnnotatedElement}.
6665
*
6766
* <p><strong>Find semantics</strong> are much more exhaustive, providing
6867
* <em>get semantics</em> plus support for the following:
@@ -76,14 +75,13 @@
7675
* </ul>
7776
*
7877
* <h3>Support for {@code @Inherited}</h3>
79-
* <p>Methods following <em>get semantics</em> will honor the contract of
80-
* Java's {@link java.lang.annotation.Inherited @Inherited} annotation except
81-
* that locally declared annotations (including custom composed annotations)
82-
* will be favored over inherited annotations. In contrast, methods following
83-
* <em>find semantics</em> will completely ignore the presence of
84-
* {@code @Inherited} since the <em>find</em> search algorithm manually
85-
* traverses type and method hierarchies and thereby implicitly supports
86-
* annotation inheritance without the need for {@code @Inherited}.
78+
* <p>Methods following <em>get semantics</em> will honor the contract of Java's
79+
* {@link java.lang.annotation.Inherited @Inherited} annotation except that locally
80+
* declared annotations (including custom composed annotations) will be favored over
81+
* inherited annotations. In contrast, methods following <em>find semantics</em>
82+
* will completely ignore the presence of {@code @Inherited} since the <em>find</em>
83+
* search algorithm manually traverses type and method hierarchies and thereby
84+
* implicitly supports annotation inheritance without a need for {@code @Inherited}.
8785
*
8886
* @author Phillip Webb
8987
* @author Juergen Hoeller
@@ -873,7 +871,7 @@ public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(Anno
873871
* @param annotationName the fully qualified class name of the annotation
874872
* type to find (as an alternative to {@code annotationType})
875873
* @param processor the processor to delegate to
876-
* @return the result of the processor, potentially {@code null}
874+
* @return the result of the processor (potentially {@code null})
877875
*/
878876
private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
879877
String annotationName, Processor<T> processor) {
@@ -892,7 +890,7 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? ex
892890
* @param containerType the type of the container that holds repeatable
893891
* annotations, or {@code null} if the annotation is not repeatable
894892
* @param processor the processor to delegate to
895-
* @return the result of the processor, potentially {@code null}
893+
* @return the result of the processor (potentially {@code null})
896894
* @since 4.3
897895
*/
898896
private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
@@ -923,7 +921,7 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? ex
923921
* @param processor the processor to delegate to
924922
* @param visited the set of annotated elements that have already been visited
925923
* @param metaDepth the meta-depth of the annotation
926-
* @return the result of the processor, potentially {@code null}
924+
* @return the result of the processor (potentially {@code null})
927925
*/
928926
private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
929927
String annotationName, Class<? extends Annotation> containerType, Processor<T> processor,
@@ -984,7 +982,7 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? ex
984982
* @param processor the processor to delegate to
985983
* @param visited the set of annotated elements that have already been visited
986984
* @param metaDepth the meta-depth of the annotation
987-
* @return the result of the processor, potentially {@code null}
985+
* @return the result of the processor (potentially {@code null})
988986
* @since 4.2
989987
*/
990988
private static <T> T searchWithGetSemanticsInAnnotations(AnnotatedElement element,
@@ -1053,7 +1051,7 @@ else if (currentAnnotationType == containerType) {
10531051
* @param annotationName the fully qualified class name of the annotation
10541052
* type to find (as an alternative to {@code annotationType})
10551053
* @param processor the processor to delegate to
1056-
* @return the result of the processor, potentially {@code null}
1054+
* @return the result of the processor (potentially {@code null})
10571055
* @since 4.2
10581056
*/
10591057
private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
@@ -1073,7 +1071,7 @@ private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? e
10731071
* @param containerType the type of the container that holds repeatable
10741072
* annotations, or {@code null} if the annotation is not repeatable
10751073
* @param processor the processor to delegate to
1076-
* @return the result of the processor, potentially {@code null}
1074+
* @return the result of the processor (potentially {@code null})
10771075
* @since 4.3
10781076
*/
10791077
private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
@@ -1109,7 +1107,7 @@ private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? e
11091107
* @param processor the processor to delegate to
11101108
* @param visited the set of annotated elements that have already been visited
11111109
* @param metaDepth the meta-depth of the annotation
1112-
* @return the result of the processor, potentially {@code null}
1110+
* @return the result of the processor (potentially {@code null})
11131111
* @since 4.2
11141112
*/
11151113
private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,

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

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -932,6 +932,32 @@ public static boolean isInJavaLangAnnotationPackage(String annotationType) {
932932
return (annotationType != null && annotationType.startsWith("java.lang.annotation"));
933933
}
934934

935+
/**
936+
* Check the declared attributes of the given annotation, in particular covering
937+
* Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for
938+
* {@code Class} values (instead of early {@code Class.getAnnotations() failure}.
939+
* <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)}
940+
* won't failure either (when attempted later on).
941+
* @param annotation the annotation to validate
942+
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
943+
* @since 4.3.15
944+
* @see Class#getAnnotations()
945+
* @see #getAnnotationAttributes(Annotation)
946+
*/
947+
public static void validateAnnotation(Annotation annotation) {
948+
for (Method method : getAttributeMethods(annotation.annotationType())) {
949+
Class<?> returnType = method.getReturnType();
950+
if (returnType == Class.class || returnType == Class[].class) {
951+
try {
952+
method.invoke(annotation);
953+
}
954+
catch (Throwable ex) {
955+
throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex);
956+
}
957+
}
958+
}
959+
}
960+
935961
/**
936962
* Retrieve the given annotation's attributes as a {@link Map}, preserving all
937963
* attribute types.
@@ -1882,13 +1908,13 @@ static void handleIntrospectionFailure(AnnotatedElement element, Throwable ex) {
18821908
if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) {
18831909
// Meta-annotation or (default) value lookup on an annotation type
18841910
if (loggerToUse.isDebugEnabled()) {
1885-
loggerToUse.debug("Failed to meta-introspect annotation [" + element + "]: " + ex);
1911+
loggerToUse.debug("Failed to meta-introspect annotation " + element + ": " + ex);
18861912
}
18871913
}
18881914
else {
18891915
// Direct annotation lookup on regular Class, Method, Field
18901916
if (loggerToUse.isInfoEnabled()) {
1891-
loggerToUse.info("Failed to introspect annotations on [" + element + "]: " + ex);
1917+
loggerToUse.info("Failed to introspect annotations on " + element + ": " + ex);
18921918
}
18931919
}
18941920
}

0 commit comments

Comments
 (0)