1
1
/*
2
- * Copyright 2002-2020 the original author or authors.
2
+ * Copyright 2002-2022 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
20
20
import java .util .ArrayDeque ;
21
21
import java .util .ArrayList ;
22
22
import java .util .Deque ;
23
+ import java .util .HashSet ;
23
24
import java .util .List ;
24
25
import java .util .Map ;
26
+ import java .util .Set ;
25
27
26
28
import org .springframework .lang .Nullable ;
27
29
import org .springframework .util .ConcurrentReferenceHashMap ;
40
42
* be searched once, regardless of how many times they are actually used.
41
43
*
42
44
* @author Phillip Webb
45
+ * @author Sam Brannen
43
46
* @since 5.2
44
47
* @see AnnotationTypeMapping
45
48
*/
@@ -60,19 +63,22 @@ final class AnnotationTypeMappings {
60
63
61
64
62
65
private AnnotationTypeMappings (RepeatableContainers repeatableContainers ,
63
- AnnotationFilter filter , Class <? extends Annotation > annotationType ) {
66
+ AnnotationFilter filter , Class <? extends Annotation > annotationType ,
67
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
64
68
65
69
this .repeatableContainers = repeatableContainers ;
66
70
this .filter = filter ;
67
71
this .mappings = new ArrayList <>();
68
- addAllMappings (annotationType );
72
+ addAllMappings (annotationType , visitedAnnotationTypes );
69
73
this .mappings .forEach (AnnotationTypeMapping ::afterAllMappingsSet );
70
74
}
71
75
72
76
73
- private void addAllMappings (Class <? extends Annotation > annotationType ) {
77
+ private void addAllMappings (Class <? extends Annotation > annotationType ,
78
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
79
+
74
80
Deque <AnnotationTypeMapping > queue = new ArrayDeque <>();
75
- addIfPossible (queue , null , annotationType , null );
81
+ addIfPossible (queue , null , annotationType , null , visitedAnnotationTypes );
76
82
while (!queue .isEmpty ()) {
77
83
AnnotationTypeMapping mapping = queue .removeFirst ();
78
84
this .mappings .add (mapping );
@@ -102,14 +108,15 @@ private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, Annot
102
108
}
103
109
104
110
private void addIfPossible (Deque <AnnotationTypeMapping > queue , AnnotationTypeMapping source , Annotation ann ) {
105
- addIfPossible (queue , source , ann .annotationType (), ann );
111
+ addIfPossible (queue , source , ann .annotationType (), ann , new HashSet <>() );
106
112
}
107
113
108
114
private void addIfPossible (Deque <AnnotationTypeMapping > queue , @ Nullable AnnotationTypeMapping source ,
109
- Class <? extends Annotation > annotationType , @ Nullable Annotation ann ) {
115
+ Class <? extends Annotation > annotationType , @ Nullable Annotation ann ,
116
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
110
117
111
118
try {
112
- queue .addLast (new AnnotationTypeMapping (source , annotationType , ann ));
119
+ queue .addLast (new AnnotationTypeMapping (source , annotationType , ann , visitedAnnotationTypes ));
113
120
}
114
121
catch (Exception ex ) {
115
122
AnnotationUtils .rethrowAnnotationConfigurationException (ex );
@@ -166,20 +173,22 @@ AnnotationTypeMapping get(int index) {
166
173
* @return type mappings for the annotation type
167
174
*/
168
175
static AnnotationTypeMappings forAnnotationType (Class <? extends Annotation > annotationType ) {
169
- return forAnnotationType (annotationType , AnnotationFilter . PLAIN );
176
+ return forAnnotationType (annotationType , new HashSet <>() );
170
177
}
171
178
172
179
/**
173
180
* Create {@link AnnotationTypeMappings} for the specified annotation type.
174
181
* @param annotationType the source annotation type
175
- * @param annotationFilter the annotation filter used to limit which
176
- * annotations are considered
182
+ * @param visitedAnnotationTypes the set of annotations that we have already
183
+ * visited; used to avoid infinite recursion for recursive annotations which
184
+ * some JVM languages support (such as Kotlin)
177
185
* @return type mappings for the annotation type
178
186
*/
179
- static AnnotationTypeMappings forAnnotationType (
180
- Class <? extends Annotation > annotationType , AnnotationFilter annotationFilter ) {
187
+ static AnnotationTypeMappings forAnnotationType (Class <? extends Annotation > annotationType ,
188
+ Set < Class <? extends Annotation >> visitedAnnotationTypes ) {
181
189
182
- return forAnnotationType (annotationType , RepeatableContainers .standardRepeatables (), annotationFilter );
190
+ return forAnnotationType (annotationType , RepeatableContainers .standardRepeatables (),
191
+ AnnotationFilter .PLAIN , visitedAnnotationTypes );
183
192
}
184
193
185
194
/**
@@ -194,15 +203,34 @@ static AnnotationTypeMappings forAnnotationType(
194
203
static AnnotationTypeMappings forAnnotationType (Class <? extends Annotation > annotationType ,
195
204
RepeatableContainers repeatableContainers , AnnotationFilter annotationFilter ) {
196
205
206
+ return forAnnotationType (annotationType , repeatableContainers , annotationFilter , new HashSet <>());
207
+ }
208
+
209
+ /**
210
+ * Create {@link AnnotationTypeMappings} for the specified annotation type.
211
+ * @param annotationType the source annotation type
212
+ * @param repeatableContainers the repeatable containers that may be used by
213
+ * the meta-annotations
214
+ * @param annotationFilter the annotation filter used to limit which
215
+ * annotations are considered
216
+ * @param visitedAnnotationTypes the set of annotations that we have already
217
+ * visited; used to avoid infinite recursion for recursive annotations which
218
+ * some JVM languages support (such as Kotlin)
219
+ * @return type mappings for the annotation type
220
+ */
221
+ private static AnnotationTypeMappings forAnnotationType (Class <? extends Annotation > annotationType ,
222
+ RepeatableContainers repeatableContainers , AnnotationFilter annotationFilter ,
223
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
224
+
197
225
if (repeatableContainers == RepeatableContainers .standardRepeatables ()) {
198
226
return standardRepeatablesCache .computeIfAbsent (annotationFilter ,
199
- key -> new Cache (repeatableContainers , key )).get (annotationType );
227
+ key -> new Cache (repeatableContainers , key )).get (annotationType , visitedAnnotationTypes );
200
228
}
201
229
if (repeatableContainers == RepeatableContainers .none ()) {
202
230
return noRepeatablesCache .computeIfAbsent (annotationFilter ,
203
- key -> new Cache (repeatableContainers , key )).get (annotationType );
231
+ key -> new Cache (repeatableContainers , key )).get (annotationType , visitedAnnotationTypes );
204
232
}
205
- return new AnnotationTypeMappings (repeatableContainers , annotationFilter , annotationType );
233
+ return new AnnotationTypeMappings (repeatableContainers , annotationFilter , annotationType , visitedAnnotationTypes );
206
234
}
207
235
208
236
static void clearCache () {
@@ -235,14 +263,21 @@ private static class Cache {
235
263
/**
236
264
* Get or create {@link AnnotationTypeMappings} for the specified annotation type.
237
265
* @param annotationType the annotation type
266
+ * @param visitedAnnotationTypes the set of annotations that we have already
267
+ * visited; used to avoid infinite recursion for recursive annotations which
268
+ * some JVM languages support (such as Kotlin)
238
269
* @return a new or existing {@link AnnotationTypeMappings} instance
239
270
*/
240
- AnnotationTypeMappings get (Class <? extends Annotation > annotationType ) {
241
- return this .mappings .computeIfAbsent (annotationType , this ::createMappings );
271
+ AnnotationTypeMappings get (Class <? extends Annotation > annotationType ,
272
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
273
+
274
+ return this .mappings .computeIfAbsent (annotationType , key -> createMappings (key , visitedAnnotationTypes ));
242
275
}
243
276
244
- AnnotationTypeMappings createMappings (Class <? extends Annotation > annotationType ) {
245
- return new AnnotationTypeMappings (this .repeatableContainers , this .filter , annotationType );
277
+ private AnnotationTypeMappings createMappings (Class <? extends Annotation > annotationType ,
278
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
279
+
280
+ return new AnnotationTypeMappings (this .repeatableContainers , this .filter , annotationType , visitedAnnotationTypes );
246
281
}
247
282
}
248
283
0 commit comments