@@ -91,8 +91,9 @@ public abstract class AnnotationUtils {
91
91
* Get a single {@link Annotation} of {@code annotationType} from the supplied
92
92
* annotation: either the given annotation itself or a direct meta-annotation
93
93
* thereof.
94
- * <p>Note that this method does <em>not</em> support arbitrary levels of
95
- * meta-annotations.
94
+ * <p>Note that this method supports only a single level of meta-annotations.
95
+ * For support for arbitrary levels of meta-annotations, use one of the
96
+ * {@code find*()} methods instead.
96
97
* @param ann the Annotation to check
97
98
* @param annotationType the annotation type to look for, both locally and as a meta-annotation
98
99
* @return the matching annotation, or {@code null} if not found
@@ -115,9 +116,11 @@ public static <T extends Annotation> T getAnnotation(Annotation ann, Class<T> an
115
116
116
117
/**
117
118
* Get a single {@link Annotation} of {@code annotationType} from the supplied
118
- * {@link AnnotatedElement}.
119
- * <p>Meta-annotations will be searched if the annotation is not
120
- * <em>directly present</em> on the supplied element.
119
+ * {@link AnnotatedElement}, where the {@code AnnotatedElement} is either
120
+ * directly annotated or meta-annotated with the {@code annotationType}.
121
+ * <p>Note that this method supports only a single level of meta-annotations.
122
+ * For support for arbitrary levels of meta-annotations, use
123
+ * {@link #findAnnotation(AnnotatedElement, Class)} instead.
121
124
* @param annotatedElement the {@code AnnotatedElement} from which to get the annotation
122
125
* @param annotationType the annotation type to look for, both locally and as a meta-annotation
123
126
* @return the matching annotation, or {@code null} if not found
@@ -144,10 +147,13 @@ public static <T extends Annotation> T getAnnotation(AnnotatedElement annotatedE
144
147
}
145
148
146
149
/**
147
- * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method}.
150
+ * Get a single {@link Annotation} of {@code annotationType} from the
151
+ * supplied {@link Method}, where the method is either directly annotated
152
+ * or meta-annotated with the {@code annotationType}.
148
153
* <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
149
- * <p>Meta-annotations will be searched if the annotation is not
150
- * <em>directly present</em> on the supplied method.
154
+ * <p>Note that this method supports only a single level of meta-annotations.
155
+ * For support for arbitrary levels of meta-annotations, use
156
+ * {@link #findAnnotation(Method, Class)} instead.
151
157
* @param method the method to look for annotations on
152
158
* @param annotationType the annotation type to look for
153
159
* @return the matching annotation, or {@code null} if not found
@@ -256,34 +262,100 @@ public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedEle
256
262
}
257
263
258
264
/**
259
- * Find a single {@link Annotation} of {@code annotationType} from the supplied
265
+ * Find a single {@link Annotation} of {@code annotationType} on the
266
+ * supplied {@link AnnotatedElement}.
267
+ * <p>Meta-annotations will be searched if the annotation is not
268
+ * <em>directly present</em> on the supplied element.
269
+ * <p><strong>Warning</strong>: this method operates generically on
270
+ * annotated elements. In other words, this method does not execute
271
+ * specialized search algorithms for classes or methods. If you require
272
+ * the more specific semantics of {@link #findAnnotation(Class, Class)}
273
+ * or {@link #findAnnotation(Method, Class)}, invoke one of those methods
274
+ * instead.
275
+ * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation
276
+ * @param annotationType the annotation type to look for, both locally and as a meta-annotation
277
+ * @return the matching annotation, or {@code null} if not found
278
+ * @since 4.2
279
+ */
280
+ public static <A extends Annotation > A findAnnotation (AnnotatedElement annotatedElement , Class <A > annotationType ) {
281
+ // Do NOT store result in the findAnnotationCache since doing so could break
282
+ // findAnnotation(Class, Class) and findAnnotation(Method, Class).
283
+ return findAnnotation (annotatedElement , annotationType , new HashSet <Annotation >());
284
+ }
285
+
286
+ /**
287
+ * Perform the search algorithm for {@link #findAnnotation(AnnotatedElement, Class)}
288
+ * avoiding endless recursion by tracking which annotations have already
289
+ * been <em>visited</em>.
290
+ * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation
291
+ * @param annotationType the annotation type to look for, both locally and as a meta-annotation
292
+ * @param visited the set of annotations that have already been visited
293
+ * @return the matching annotation, or {@code null} if not found
294
+ * @since 4.2
295
+ */
296
+ @ SuppressWarnings ("unchecked" )
297
+ private static <T extends Annotation > T findAnnotation (AnnotatedElement annotatedElement , Class <T > annotationType , Set <Annotation > visited ) {
298
+ Assert .notNull (annotatedElement , "AnnotatedElement must not be null" );
299
+ try {
300
+ Annotation [] anns = annotatedElement .getDeclaredAnnotations ();
301
+ for (Annotation ann : anns ) {
302
+ if (ann .annotationType ().equals (annotationType )) {
303
+ return (T ) ann ;
304
+ }
305
+ }
306
+ for (Annotation ann : anns ) {
307
+ if (!isInJavaLangAnnotationPackage (ann ) && visited .add (ann )) {
308
+ T annotation = findAnnotation ((AnnotatedElement ) ann .annotationType (), annotationType , visited );
309
+ if (annotation != null ) {
310
+ return annotation ;
311
+ }
312
+ }
313
+ }
314
+ }
315
+ catch (Exception ex ) {
316
+ // Assuming nested Class values not resolvable within annotation attributes...
317
+ logIntrospectionFailure (annotatedElement , ex );
318
+ }
319
+ return null ;
320
+ }
321
+
322
+ /**
323
+ * Find a single {@link Annotation} of {@code annotationType} on the supplied
260
324
* {@link Method}, traversing its super methods (i.e., from superclasses and
261
325
* interfaces) if no annotation can be found on the given method itself.
326
+ * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
327
+ * <p>Meta-annotations will be searched if the annotation is not
328
+ * <em>directly present</em> on the method.
262
329
* <p>Annotations on methods are not inherited by default, so we need to handle
263
330
* this explicitly.
264
- * <p>Meta-annotations will <em>not</em> be searched.
265
331
* @param method the method to look for annotations on
266
332
* @param annotationType the annotation type to look for
267
333
* @return the matching annotation, or {@code null} if not found
334
+ * @see #getAnnotation(Method, Class)
268
335
*/
269
336
@ SuppressWarnings ("unchecked" )
270
337
public static <A extends Annotation > A findAnnotation (Method method , Class <A > annotationType ) {
271
338
AnnotationCacheKey cacheKey = new AnnotationCacheKey (method , annotationType );
272
339
A result = (A ) findAnnotationCache .get (cacheKey );
340
+
273
341
if (result == null ) {
274
- result = getAnnotation (method , annotationType );
275
- Class <?> clazz = method .getDeclaringClass ();
342
+ Method resolvedMethod = BridgeMethodResolver .findBridgedMethod (method );
343
+ result = findAnnotation ((AnnotatedElement ) resolvedMethod , annotationType );
344
+
276
345
if (result == null ) {
277
- result = searchOnInterfaces (method , annotationType , clazz .getInterfaces ());
346
+ result = searchOnInterfaces (method , annotationType , method . getDeclaringClass () .getInterfaces ());
278
347
}
348
+
349
+ Class <?> clazz = method .getDeclaringClass ();
279
350
while (result == null ) {
280
351
clazz = clazz .getSuperclass ();
281
352
if (clazz == null || clazz .equals (Object .class )) {
282
353
break ;
283
354
}
284
355
try {
285
356
Method equivalentMethod = clazz .getDeclaredMethod (method .getName (), method .getParameterTypes ());
286
- result = getAnnotation (equivalentMethod , annotationType );
357
+ Method resolvedEquivalentMethod = BridgeMethodResolver .findBridgedMethod (equivalentMethod );
358
+ result = findAnnotation ((AnnotatedElement ) resolvedEquivalentMethod , annotationType );
287
359
}
288
360
catch (NoSuchMethodException ex ) {
289
361
// No equivalent method found
@@ -292,9 +364,10 @@ public static <A extends Annotation> A findAnnotation(Method method, Class<A> an
292
364
result = searchOnInterfaces (method , annotationType , clazz .getInterfaces ());
293
365
}
294
366
}
295
- if (result != null ) {
296
- findAnnotationCache .put (cacheKey , result );
297
- }
367
+ }
368
+
369
+ if (result != null ) {
370
+ findAnnotationCache .put (cacheKey , result );
298
371
}
299
372
return result ;
300
373
}
@@ -680,7 +753,7 @@ else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
680
753
}
681
754
682
755
/**
683
- * Retrieve the <em>value</em> of the {@code " value" } attribute of a
756
+ * Retrieve the <em>value</em> of the {@code value} attribute of a
684
757
* single-element Annotation, given an annotation instance.
685
758
* @param annotation the annotation instance from which to retrieve the value
686
759
* @return the attribute value, or {@code null} if not found
@@ -712,7 +785,7 @@ public static Object getValue(Annotation annotation, String attributeName) {
712
785
}
713
786
714
787
/**
715
- * Retrieve the <em>default value</em> of the {@code " value" } attribute
788
+ * Retrieve the <em>default value</em> of the {@code value} attribute
716
789
* of a single-element Annotation, given an annotation instance.
717
790
* @param annotation the annotation instance from which to retrieve the default value
718
791
* @return the default value, or {@code null} if not found
@@ -737,7 +810,7 @@ public static Object getDefaultValue(Annotation annotation, String attributeName
737
810
}
738
811
739
812
/**
740
- * Retrieve the <em>default value</em> of the {@code " value" } attribute
813
+ * Retrieve the <em>default value</em> of the {@code value} attribute
741
814
* of a single-element Annotation, given the {@link Class annotation type}.
742
815
* @param annotationType the <em>annotation type</em> for which the default value should be retrieved
743
816
* @return the default value, or {@code null} if not found
0 commit comments