Skip to content

Commit 979c483

Browse files
committed
Do not inspect meta-annotations on Java annotations
This commit introduces a new isInJavaLangAnnotationPackage(Annotation) method in AnnotationUtils. This method is now used in AnnotationUtils, AnnotatedElementUtils, and MetaAnnotationUtils to ensure that search algorithms do no search for meta-annotations on annotations in the "java.lang.annotation" package. The following are some empirical results from this change: - The number of times that the findAnnotation(Class,Class,Set) method in AnnotationUtils is recursively invoked while executing AnnotationUtilsTests drops from 51 to 29. - The number of times that the process(AnnotatedElement) method in AnnotationUtils.AnnotationCollector is recursively invoked while executing AnnotationUtilsTests.getRepeatableFromMethod() drops from 16 to 2. - The number of times that the doProcess() method in AnnotatedElementUtils is recursively invoked while executing the "getAnnotationAttributes() On MetaCycleAnnotatedClass with missing target meta-annotation" test in AnnotatedElementUtilsTests drops from 23 to 5. - The number of times that the findAnnotationDescriptor(Class,Set,Class) method in MetaAnnotationUtils is recursively invoked while executing the "findAnnotationDescriptor() on MetaCycleAnnotatedClass with missing target meta-annotation" test in MetaAnnotationUtilsTests drops from 16 to 8. Issue: SPR-11483
1 parent 651e0a4 commit 979c483

File tree

6 files changed

+92
-10
lines changed

6 files changed

+92
-10
lines changed

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

Lines changed: 9 additions & 6 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.
@@ -32,6 +32,7 @@
3232
*
3333
* @author Phillip Webb
3434
* @author Juergen Hoeller
35+
* @author Sam Brannen
3536
* @since 4.0
3637
*/
3738
public class AnnotatedElementUtils {
@@ -159,7 +160,7 @@ private static <T> T doProcess(AnnotatedElement element, String annotationType,
159160
for (Annotation annotation : element.getAnnotations()) {
160161
if (annotation.annotationType().getName().equals(annotationType) || depth > 0) {
161162
T result = processor.process(annotation, depth);
162-
if (result != null) {
163+
if (result != null) {
163164
return result;
164165
}
165166
result = doProcess(annotation.annotationType(), annotationType, processor, visited, depth + 1);
@@ -170,10 +171,12 @@ private static <T> T doProcess(AnnotatedElement element, String annotationType,
170171
}
171172
}
172173
for (Annotation annotation : element.getAnnotations()) {
173-
T result = doProcess(annotation.annotationType(), annotationType, processor, visited, depth);
174-
if (result != null) {
175-
processor.postProcess(annotation, result);
176-
return result;
174+
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
175+
T result = doProcess(annotation.annotationType(), annotationType, processor, visited, depth);
176+
if (result != null) {
177+
processor.postProcess(annotation, result);
178+
return result;
179+
}
177180
}
178181
}
179182
}

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A>
289289
}
290290
}
291291
for (Annotation ann : clazz.getAnnotations()) {
292-
if (visited.add(ann)) {
292+
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
293293
annotation = findAnnotation(ann.annotationType(), annotationType, visited);
294294
if (annotation != null) {
295295
return annotation;
@@ -422,6 +422,18 @@ public static boolean isAnnotationInherited(Class<? extends Annotation> annotati
422422
return (clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz));
423423
}
424424

425+
/**
426+
* Determine if the supplied {@link Annotation} is defined in the
427+
* {@code java.lang.annotation} package.
428+
*
429+
* @param annotation the annotation to check; never {@code null}
430+
* @return {@code true} if the annotation is in the {@code java.lang.annotation} package
431+
*/
432+
public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
433+
Assert.notNull(annotation, "Annotation must not be null");
434+
return annotation.annotationType().getName().startsWith("java.lang.annotation");
435+
}
436+
425437
/**
426438
* Retrieve the given annotation's attributes as a Map, preserving all attribute types
427439
* as-is.
@@ -631,7 +643,7 @@ private void process(AnnotatedElement annotatedElement) {
631643
else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, annotation.annotationType())) {
632644
result.addAll(Arrays.asList(getValue(annotation)));
633645
}
634-
else {
646+
else if (!isInJavaLangAnnotationPackage(annotation)) {
635647
process(annotation.annotationType());
636648
}
637649
}

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616

1717
package org.springframework.core.annotation;
1818

19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
1921
import java.lang.annotation.Inherited;
2022
import java.lang.annotation.Retention;
2123
import java.lang.annotation.RetentionPolicy;
24+
import java.lang.annotation.Target;
2225

2326
import org.junit.Test;
2427

@@ -32,6 +35,13 @@
3235
*/
3336
public class AnnotatedElementUtilsTests {
3437

38+
@Test
39+
public void getAnnotationAttributesOnMetaCycleAnnotatedClassWithMissingTargetMetaAnnotation() {
40+
AnnotationAttributes attributes = AnnotatedElementUtils.getAnnotationAttributes(MetaCycleAnnotatedClass.class,
41+
Transactional.class.getName());
42+
assertNull("Should not find annotation attributes for @Transactional on MetaCycleAnnotatedClass", attributes);
43+
}
44+
3545
@Test
3646
public void getAnnotationAttributesFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
3747
AnnotationAttributes attributes = AnnotatedElementUtils.getAnnotationAttributes(
@@ -61,7 +71,34 @@ public void getAnnotationAttributesFavorsInheritedComposedAnnotationsOverMoreLoc
6171

6272
// -------------------------------------------------------------------------
6373

74+
@MetaCycle3
75+
@Retention(RetentionPolicy.RUNTIME)
76+
@Target(ElementType.ANNOTATION_TYPE)
77+
@Documented
78+
@interface MetaCycle1 {
79+
}
80+
81+
@MetaCycle1
82+
@Retention(RetentionPolicy.RUNTIME)
83+
@Target(ElementType.ANNOTATION_TYPE)
84+
@Documented
85+
@interface MetaCycle2 {
86+
}
87+
88+
@MetaCycle2
89+
@Retention(RetentionPolicy.RUNTIME)
90+
@Target(ElementType.TYPE)
91+
@Documented
92+
@interface MetaCycle3 {
93+
}
94+
95+
@MetaCycle3
96+
static class MetaCycleAnnotatedClass {
97+
}
98+
6499
@Retention(RetentionPolicy.RUNTIME)
100+
@Target(ElementType.TYPE)
101+
@Documented
65102
@Inherited
66103
@interface Transactional {
67104

@@ -70,12 +107,16 @@ public void getAnnotationAttributesFavorsInheritedComposedAnnotationsOverMoreLoc
70107

71108
@Transactional
72109
@Retention(RetentionPolicy.RUNTIME)
110+
@Target(ElementType.TYPE)
111+
@Documented
73112
@Inherited
74113
@interface Composed1 {
75114
}
76115

77116
@Transactional(readOnly = true)
78117
@Retention(RetentionPolicy.RUNTIME)
118+
@Target(ElementType.TYPE)
119+
@Documented
79120
@interface Composed2 {
80121
}
81122

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,13 @@ public void findAnnotationFromInterfaceWhenSuperDoesNotImplementMethod() throws
362362
assertNotNull(order);
363363
}
364364

365+
@Test
366+
public void findRepeatableAnnotationOnComposedAnnotation() {
367+
Repeatable repeatable = findAnnotation(MyRepeatableMeta.class, Repeatable.class);
368+
assertNotNull(repeatable);
369+
assertEquals(MyRepeatableContainer.class, repeatable.value());
370+
}
371+
365372
@Test
366373
public void getRepeatableFromMethod() throws Exception {
367374
Method method = InterfaceWithRepeated.class.getMethod("foo");

spring-test/src/main/java/org/springframework/test/context/MetaAnnotationUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ private static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDesc
123123

124124
// Declared on a composed annotation (i.e., as a meta-annotation)?
125125
for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) {
126-
if (visited.add(composedAnnotation)) {
126+
if (!isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
127127
AnnotationDescriptor<T> descriptor = findAnnotationDescriptor(composedAnnotation.annotationType(),
128128
visited, annotationType);
129129
if (descriptor != null) {
@@ -210,7 +210,7 @@ private static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Clas
210210

211211
// Declared on a composed annotation (i.e., as a meta-annotation)?
212212
for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) {
213-
if (visited.add(composedAnnotation)) {
213+
if (!isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
214214
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(
215215
composedAnnotation.annotationType(), visited, annotationTypes);
216216
if (descriptor != null) {

spring-test/src/test/java/org/springframework/test/context/MetaAnnotationUtilsTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
package org.springframework.test.context;
1818

1919
import java.lang.annotation.Annotation;
20+
import java.lang.annotation.Documented;
21+
import java.lang.annotation.ElementType;
2022
import java.lang.annotation.Retention;
2123
import java.lang.annotation.RetentionPolicy;
24+
import java.lang.annotation.Target;
2225

2326
import org.junit.Test;
2427
import org.springframework.core.annotation.Order;
@@ -393,42 +396,58 @@ public void findAnnotationDescriptorForTypesOnMetaCycleAnnotatedClassWithMissing
393396
@Component(value = "meta1")
394397
@Order
395398
@Retention(RetentionPolicy.RUNTIME)
399+
@Target(ElementType.TYPE)
400+
@Documented
396401
static @interface Meta1 {
397402
}
398403

399404
@Component(value = "meta2")
400405
@Transactional
401406
@Retention(RetentionPolicy.RUNTIME)
407+
@Target(ElementType.TYPE)
408+
@Documented
402409
static @interface Meta2 {
403410
}
404411

405412
@Meta2
406413
@Retention(RetentionPolicy.RUNTIME)
414+
@Target(ElementType.TYPE)
415+
@Documented
407416
@interface MetaMeta {
408417
}
409418

410419
@MetaMeta
411420
@Retention(RetentionPolicy.RUNTIME)
421+
@Target(ElementType.TYPE)
422+
@Documented
412423
@interface MetaMetaMeta {
413424
}
414425

415426
@MetaCycle3
416427
@Retention(RetentionPolicy.RUNTIME)
428+
@Target(ElementType.ANNOTATION_TYPE)
429+
@Documented
417430
@interface MetaCycle1 {
418431
}
419432

420433
@MetaCycle1
421434
@Retention(RetentionPolicy.RUNTIME)
435+
@Target(ElementType.ANNOTATION_TYPE)
436+
@Documented
422437
@interface MetaCycle2 {
423438
}
424439

425440
@MetaCycle2
426441
@Retention(RetentionPolicy.RUNTIME)
442+
@Target(ElementType.TYPE)
443+
@Documented
427444
@interface MetaCycle3 {
428445
}
429446

430447
@ContextConfiguration
431448
@Retention(RetentionPolicy.RUNTIME)
449+
@Target(ElementType.TYPE)
450+
@Documented
432451
static @interface MetaConfig {
433452

434453
static class DevConfig {

0 commit comments

Comments
 (0)