Skip to content

Commit a2f1169

Browse files
committed
Don't detect annotations on superclass in StAnMeta
Changes introduced in conjunction with issue SPR-11475 altered the behavior of StandardAnnotationMetadata such that annotations could be detected on superclasses, specifically in the case where the AnnotatedElementUtils.getAllAnnotationAttributes() method is invoked to obtain multiple annotations of the same type (on the lowest level in the class hierarchy), as is the case for @Profile and @conditional. This commit partially reverts these changes as follows: - All methods in AnnotatedElementUtils now set the traverseClassHierarchy to false, thereby effectively reverting the changes made in commit 1d30bf8. Note, however, that the changes made to AnnotationUtils remain in place. - Introduced tests in AnnotationMetadataTests that verify behavior present in Spring Framework 4.0.2 and earlier. - Updated tests in AnnotatedElementUtilsTests so that they pass against the reverted changes (i.e., align with the behavior present in Spring Framework 4.0.2 and earlier). - Refined Javadoc in AnnotationMetadata with regard to annotations being "present" vs. "defined". - Refined Javadoc in AnnotatedTypeMetadata. Issue: SPR-11475, SPR-11595
1 parent d970015 commit a2f1169

File tree

5 files changed

+86
-31
lines changed

5 files changed

+86
-31
lines changed

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

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626
import org.springframework.util.LinkedMultiValueMap;
2727
import org.springframework.util.MultiValueMap;
2828

29-
import static org.springframework.core.annotation.AnnotationUtils.*;
30-
3129
/**
3230
* Utility class used to collect all annotation values including those declared on
3331
* meta-annotations.
@@ -41,14 +39,16 @@ public class AnnotatedElementUtils {
4139

4240
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
4341
final Set<String> types = new LinkedHashSet<String>();
44-
process(element, annotationType, true, new Processor<Object>() {
42+
process(element, annotationType, false, new Processor<Object>() {
43+
4544
@Override
4645
public Object process(Annotation annotation, int metaDepth) {
4746
if (metaDepth > 0) {
4847
types.add(annotation.annotationType().getName());
4948
}
5049
return null;
5150
}
51+
5252
@Override
5353
public void postProcess(Annotation annotation, Object result) {
5454
}
@@ -57,26 +57,30 @@ public void postProcess(Annotation annotation, Object result) {
5757
}
5858

5959
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
60-
return Boolean.TRUE.equals(process(element, annotationType, true, new Processor<Boolean>() {
60+
return Boolean.TRUE.equals(process(element, annotationType, false, new Processor<Boolean>() {
61+
6162
@Override
6263
public Boolean process(Annotation annotation, int metaDepth) {
6364
if (metaDepth > 0) {
6465
return Boolean.TRUE;
6566
}
6667
return null;
6768
}
69+
6870
@Override
6971
public void postProcess(Annotation annotation, Boolean result) {
7072
}
7173
}));
7274
}
7375

7476
public static boolean isAnnotated(AnnotatedElement element, String annotationType) {
75-
return Boolean.TRUE.equals(process(element, annotationType, true, new Processor<Boolean>() {
77+
return Boolean.TRUE.equals(process(element, annotationType, false, new Processor<Boolean>() {
78+
7679
@Override
7780
public Boolean process(Annotation annotation, int metaDepth) {
7881
return Boolean.TRUE;
7982
}
83+
8084
@Override
8185
public void postProcess(Annotation annotation, Boolean result) {
8286
}
@@ -90,16 +94,18 @@ public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement elem
9094
public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationType,
9195
final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
9296

93-
return process(element, annotationType, true, new Processor<AnnotationAttributes>() {
97+
return process(element, annotationType, false, new Processor<AnnotationAttributes>() {
98+
9499
@Override
95100
public AnnotationAttributes process(Annotation annotation, int metaDepth) {
96101
return AnnotationUtils.getAnnotationAttributes(annotation, classValuesAsString, nestedAnnotationsAsMap);
97102
}
103+
98104
@Override
99105
public void postProcess(Annotation annotation, AnnotationAttributes result) {
100106
for (String key : result.keySet()) {
101-
if (!VALUE.equals(key)) {
102-
Object value = getValue(annotation, key);
107+
if (!AnnotationUtils.VALUE.equals(key)) {
108+
Object value = AnnotationUtils.getValue(annotation, key);
103109
if (value != null) {
104110
result.put(key, value);
105111
}
@@ -109,7 +115,8 @@ public void postProcess(Annotation annotation, AnnotationAttributes result) {
109115
});
110116
}
111117

112-
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationType) {
118+
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
119+
String annotationType) {
113120
return getAllAnnotationAttributes(element, annotationType, false, false);
114121
}
115122

@@ -118,6 +125,7 @@ public static MultiValueMap<String, Object> getAllAnnotationAttributes(Annotated
118125

119126
final MultiValueMap<String, Object> attributes = new LinkedMultiValueMap<String, Object>();
120127
process(element, annotationType, false, new Processor<Void>() {
128+
121129
@Override
122130
public Void process(Annotation annotation, int metaDepth) {
123131
if (annotation.annotationType().getName().equals(annotationType)) {
@@ -128,11 +136,12 @@ public Void process(Annotation annotation, int metaDepth) {
128136
}
129137
return null;
130138
}
139+
131140
@Override
132141
public void postProcess(Annotation annotation, Void result) {
133142
for (String key : attributes.keySet()) {
134-
if (!VALUE.equals(key)) {
135-
Object value = getValue(annotation, key);
143+
if (!AnnotationUtils.VALUE.equals(key)) {
144+
Object value = AnnotationUtils.getValue(annotation, key);
136145
if (value != null) {
137146
attributes.add(key, value);
138147
}
@@ -161,7 +170,8 @@ private static <T> T process(AnnotatedElement element, String annotationType, bo
161170
Processor<T> processor) {
162171

163172
try {
164-
return doProcess(element, annotationType, traverseClassHierarchy, processor, new HashSet<AnnotatedElement>(), 0);
173+
return doProcess(element, annotationType, traverseClassHierarchy, processor,
174+
new HashSet<AnnotatedElement>(), 0);
165175
}
166176
catch (Throwable ex) {
167177
throw new IllegalStateException("Failed to introspect annotations: " + element, ex);
@@ -189,8 +199,8 @@ private static <T> T doProcess(AnnotatedElement element, String annotationType,
189199
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
190200

191201
if (visited.add(element)) {
192-
Annotation[] annotations =
193-
(traverseClassHierarchy ? element.getDeclaredAnnotations() : element.getAnnotations());
202+
Annotation[] annotations = (traverseClassHierarchy ? element.getDeclaredAnnotations()
203+
: element.getAnnotations());
194204
for (Annotation annotation : annotations) {
195205
if (annotation.annotationType().getName().equals(annotationType) || metaDepth > 0) {
196206
T result = processor.process(annotation, metaDepth);
@@ -206,7 +216,7 @@ private static <T> T doProcess(AnnotatedElement element, String annotationType,
206216
}
207217
}
208218
for (Annotation annotation : annotations) {
209-
if (!isInJavaLangAnnotationPackage(annotation)) {
219+
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
210220
T result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
211221
processor, visited, metaDepth);
212222
if (result != null) {

spring-core/src/main/java/org/springframework/core/type/AnnotatedTypeMetadata.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -30,6 +30,7 @@
3030
* @author Mark Pollack
3131
* @author Chris Beams
3232
* @author Phillip Webb
33+
* @author Sam Brannen
3334
* @since 4.0
3435
* @see AnnotationMetadata
3536
* @see MethodMetadata
@@ -73,17 +74,20 @@ public interface AnnotatedTypeMetadata {
7374

7475
/**
7576
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
76-
* defined on the underlying method, as direct annotation or as meta-annotation).
77+
* defined on the underlying type ({@link AnnotationMetadata class} or
78+
* {@link MethodMetadata method}), as direct annotation or as meta-annotation).
7779
* @param annotationType the annotation type to look for
7880
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value")
7981
* and a list of the defined attribute values as Map value. This return value will
8082
* be {@code null} if no matching annotation is defined.
83+
* @see #getAllAnnotationAttributes(String, boolean)
8184
*/
8285
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType);
8386

8487
/**
8588
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
86-
* defined on the underlying method, as direct annotation or as meta-annotation).
89+
* defined on the underlying type ({@link AnnotationMetadata class} or
90+
* {@link MethodMetadata method}), as direct annotation or as meta-annotation).
8791
* @param annotationType the annotation type to look for
8892
* @param classValuesAsString whether to convert class references to String
8993
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value")

spring-core/src/main/java/org/springframework/core/type/AnnotationMetadata.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -25,6 +25,7 @@
2525
* @author Juergen Hoeller
2626
* @author Mark Fisher
2727
* @author Phillip Webb
28+
* @author Sam Brannen
2829
* @since 2.5
2930
* @see StandardAnnotationMetadata
3031
* @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata()
@@ -33,32 +34,33 @@
3334
public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
3435

3536
/**
36-
* Return the names of all annotation types defined on the underlying class.
37+
* Return the names of all annotation types that are <em>present</em> on the
38+
* underlying class.
3739
* @return the annotation type names
3840
*/
3941
Set<String> getAnnotationTypes();
4042

4143
/**
42-
* Return the names of all meta-annotation types defined on the
43-
* given annotation type of the underlying class.
44+
* Return the names of all meta-annotation types <em>present</em> on the
45+
* given annotation type on the underlying class.
4446
* @param annotationType the meta-annotation type to look for
4547
* @return the meta-annotation type names
4648
*/
4749
Set<String> getMetaAnnotationTypes(String annotationType);
4850

4951
/**
50-
* Determine whether the underlying class has an annotation of the given
51-
* type defined.
52+
* Determine whether an annotation of the given type is <em>present</em> on
53+
* the underlying class.
5254
* @param annotationType the annotation type to look for
53-
* @return whether a matching annotation is defined
55+
* @return whether a matching annotation is present
5456
*/
5557
boolean hasAnnotation(String annotationType);
5658

5759
/**
58-
* Determine whether the underlying class has an annotation that
59-
* is itself annotated with the meta-annotation of the given type.
60+
* Determine whether the underlying class has an annotation that is itself
61+
* annotated with the meta-annotation of the given type.
6062
* @param metaAnnotationType the meta-annotation type to look for
61-
* @return whether a matching meta-annotation is defined
63+
* @return whether a matching meta-annotation is present
6264
*/
6365
boolean hasMetaAnnotation(String metaAnnotationType);
6466

@@ -74,7 +76,7 @@ public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata
7476
* <p>For any returned method, {@link MethodMetadata#isAnnotated} will
7577
* return {@code true} for the given annotation type.
7678
* @param annotationType the annotation type to look for
77-
* @return a Set of {@link MethodMetadata} for methods that have a matching
79+
* @return a set of {@link MethodMetadata} for methods that have a matching
7880
* annotation. The return value will be an empty set if no methods match
7981
* the annotation type.
8082
*/

spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,10 @@ public void getAnnotationAttributesFavorsInheritedAnnotationsOverMoreLocallyDecl
103103
AnnotationAttributes attributes = getAnnotationAttributes(SubSubClassWithInheritedAnnotation.class,
104104
Transactional.class.getName());
105105
assertNotNull("AnnotationAttributes for @Transactional on SubSubClassWithInheritedAnnotation", attributes);
106-
assertEquals("readOnly flag for SubSubClassWithInheritedAnnotation.", true, attributes.getBoolean("readOnly"));
106+
107+
// TODO [SPR-11475] Set expected to true.
108+
boolean expected = false;
109+
assertEquals("readOnly flag for SubSubClassWithInheritedAnnotation.", expected, attributes.getBoolean("readOnly"));
107110
}
108111

109112
@Test
@@ -112,7 +115,10 @@ public void getAnnotationAttributesFavorsInheritedComposedAnnotationsOverMoreLoc
112115
Transactional.class.getName());
113116
assertNotNull("AnnotationAttributtes for @Transactional on SubSubClassWithInheritedComposedAnnotation.",
114117
attributes);
115-
assertEquals("readOnly flag for SubSubClassWithInheritedComposedAnnotation.", true,
118+
119+
// TODO [SPR-11475] Set expected to true.
120+
boolean expected = false;
121+
assertEquals("readOnly flag for SubSubClassWithInheritedComposedAnnotation.", expected,
116122
attributes.getBoolean("readOnly"));
117123
}
118124

spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ public void asmAnnotationMetadata() throws Exception {
6464
doTestMethodAnnotationInfo(metadata);
6565
}
6666

67+
@Test
68+
public void standardAnnotationMetadataForSubclass() throws Exception {
69+
AnnotationMetadata metadata = new StandardAnnotationMetadata(AnnotatedComponentSubClass.class, true);
70+
doTestSubClassAnnotationInfo(metadata);
71+
}
72+
73+
@Test
74+
public void asmAnnotationMetadataForSubclass() throws Exception {
75+
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
76+
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(AnnotatedComponentSubClass.class.getName());
77+
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
78+
doTestSubClassAnnotationInfo(metadata);
79+
}
80+
6781
/**
6882
* In order to preserve backward-compatibility, {@link StandardAnnotationMetadata}
6983
* defaults to return nested annotations and annotation arrays as actual
@@ -211,6 +225,21 @@ private void doTestAnnotationInfo(AnnotationMetadata metadata) {
211225
}
212226
}
213227

228+
private void doTestSubClassAnnotationInfo(AnnotationMetadata metadata) {
229+
assertThat(metadata.getClassName(), is(AnnotatedComponentSubClass.class.getName()));
230+
assertThat(metadata.isAnnotated(Component.class.getName()), is(false));
231+
assertThat(metadata.isAnnotated(Scope.class.getName()), is(false));
232+
assertThat(metadata.isAnnotated(SpecialAttr.class.getName()), is(false));
233+
assertThat(metadata.hasAnnotation(Component.class.getName()), is(false));
234+
assertThat(metadata.hasAnnotation(Scope.class.getName()), is(false));
235+
assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(false));
236+
assertThat(metadata.getAnnotationTypes().size(), is(0));
237+
assertThat(metadata.getAnnotationAttributes(Component.class.getName()), nullValue());
238+
assertThat(metadata.getAnnotatedMethods(DirectAnnotation.class.getName()).size(), equalTo(0));
239+
assertThat(metadata.isAnnotated(IsAnnotatedAnnotation.class.getName()), equalTo(false));
240+
assertThat(metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()), nullValue());
241+
}
242+
214243
private void doTestMethodAnnotationInfo(AnnotationMetadata classMetadata) {
215244
Set<MethodMetadata> methods = classMetadata.getAnnotatedMethods(TestAutowired.class.getName());
216245
assertThat(methods.size(), is(1));
@@ -317,6 +346,10 @@ public void meta() {
317346
}
318347
}
319348

349+
private static class AnnotatedComponentSubClass extends AnnotatedComponent {
350+
351+
}
352+
320353
@Target(ElementType.TYPE)
321354
@Retention(RetentionPolicy.RUNTIME)
322355
@Component

0 commit comments

Comments
 (0)