1
1
/*
2
- * Copyright 2002-2022 the original author or authors.
2
+ * Copyright 2002-2023 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,21 @@ 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 ) {
74
79
Deque <AnnotationTypeMapping > queue = new ArrayDeque <>();
75
- addIfPossible (queue , null , annotationType , null );
80
+ addIfPossible (queue , null , annotationType , null , visitedAnnotationTypes );
76
81
while (!queue .isEmpty ()) {
77
82
AnnotationTypeMapping mapping = queue .removeFirst ();
78
83
this .mappings .add (mapping );
@@ -102,14 +107,15 @@ private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, Annot
102
107
}
103
108
104
109
private void addIfPossible (Deque <AnnotationTypeMapping > queue , AnnotationTypeMapping source , Annotation ann ) {
105
- addIfPossible (queue , source , ann .annotationType (), ann );
110
+ addIfPossible (queue , source , ann .annotationType (), ann , new HashSet <>() );
106
111
}
107
112
108
113
private void addIfPossible (Deque <AnnotationTypeMapping > queue , @ Nullable AnnotationTypeMapping source ,
109
- Class <? extends Annotation > annotationType , @ Nullable Annotation ann ) {
114
+ Class <? extends Annotation > annotationType , @ Nullable Annotation ann ,
115
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
110
116
111
117
try {
112
- queue .addLast (new AnnotationTypeMapping (source , annotationType , ann ));
118
+ queue .addLast (new AnnotationTypeMapping (source , annotationType , ann , visitedAnnotationTypes ));
113
119
}
114
120
catch (Exception ex ) {
115
121
AnnotationUtils .rethrowAnnotationConfigurationException (ex );
@@ -166,20 +172,37 @@ AnnotationTypeMapping get(int index) {
166
172
* @return type mappings for the annotation type
167
173
*/
168
174
static AnnotationTypeMappings forAnnotationType (Class <? extends Annotation > annotationType ) {
169
- return forAnnotationType (annotationType , AnnotationFilter . PLAIN );
175
+ return forAnnotationType (annotationType , new HashSet <>() );
170
176
}
171
177
172
178
/**
173
179
* Create {@link AnnotationTypeMappings} for the specified annotation type.
174
180
* @param annotationType the source annotation type
181
+ * @param visitedAnnotationTypes the set of annotations that we have already
182
+ * visited; used to avoid infinite recursion for recursive annotations which
183
+ * some JVM languages support (such as Kotlin)
184
+ * @return type mappings for the annotation type
185
+ */
186
+ static AnnotationTypeMappings forAnnotationType (Class <? extends Annotation > annotationType ,
187
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
188
+
189
+ return forAnnotationType (annotationType , RepeatableContainers .standardRepeatables (),
190
+ AnnotationFilter .PLAIN , visitedAnnotationTypes );
191
+ }
192
+
193
+ /**
194
+ * Create {@link AnnotationTypeMappings} for the specified annotation type.
195
+ * @param annotationType the source annotation type
196
+ * @param repeatableContainers the repeatable containers that may be used by
197
+ * the meta-annotations
175
198
* @param annotationFilter the annotation filter used to limit which
176
199
* annotations are considered
177
200
* @return type mappings for the annotation type
178
201
*/
179
- static AnnotationTypeMappings forAnnotationType (
180
- Class <? extends Annotation > annotationType , AnnotationFilter annotationFilter ) {
202
+ static AnnotationTypeMappings forAnnotationType (Class <? extends Annotation > annotationType ,
203
+ RepeatableContainers repeatableContainers , AnnotationFilter annotationFilter ) {
181
204
182
- return forAnnotationType (annotationType , RepeatableContainers . standardRepeatables () , annotationFilter );
205
+ return forAnnotationType (annotationType , repeatableContainers , annotationFilter , new HashSet <>() );
183
206
}
184
207
185
208
/**
@@ -189,20 +212,25 @@ static AnnotationTypeMappings forAnnotationType(
189
212
* the meta-annotations
190
213
* @param annotationFilter the annotation filter used to limit which
191
214
* annotations are considered
215
+ * @param visitedAnnotationTypes the set of annotations that we have already
216
+ * visited; used to avoid infinite recursion for recursive annotations which
217
+ * some JVM languages support (such as Kotlin)
192
218
* @return type mappings for the annotation type
193
219
*/
194
220
static AnnotationTypeMappings forAnnotationType (Class <? extends Annotation > annotationType ,
195
- RepeatableContainers repeatableContainers , AnnotationFilter annotationFilter ) {
221
+ RepeatableContainers repeatableContainers , AnnotationFilter annotationFilter ,
222
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
196
223
197
224
if (repeatableContainers == RepeatableContainers .standardRepeatables ()) {
198
225
return standardRepeatablesCache .computeIfAbsent (annotationFilter ,
199
- key -> new Cache (repeatableContainers , key )).get (annotationType );
226
+ key -> new Cache (repeatableContainers , key )).get (annotationType , visitedAnnotationTypes );
200
227
}
201
228
if (repeatableContainers == RepeatableContainers .none ()) {
202
229
return noRepeatablesCache .computeIfAbsent (annotationFilter ,
203
- key -> new Cache (repeatableContainers , key )).get (annotationType );
230
+ key -> new Cache (repeatableContainers , key )).get (annotationType , visitedAnnotationTypes );
204
231
}
205
- return new AnnotationTypeMappings (repeatableContainers , annotationFilter , annotationType );
232
+ return new AnnotationTypeMappings (repeatableContainers , annotationFilter , annotationType ,
233
+ visitedAnnotationTypes );
206
234
}
207
235
208
236
static void clearCache () {
@@ -235,14 +263,20 @@ 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
+ return this .mappings .computeIfAbsent (annotationType , key -> createMappings (key , visitedAnnotationTypes ));
242
274
}
243
275
244
- AnnotationTypeMappings createMappings (Class <? extends Annotation > annotationType ) {
245
- return new AnnotationTypeMappings (this .repeatableContainers , this .filter , annotationType );
276
+ private AnnotationTypeMappings createMappings (Class <? extends Annotation > annotationType ,
277
+ Set <Class <? extends Annotation >> visitedAnnotationTypes ) {
278
+ return new AnnotationTypeMappings (this .repeatableContainers , this .filter , annotationType ,
279
+ visitedAnnotationTypes );
246
280
}
247
281
}
248
282
0 commit comments