@@ -91,8 +91,9 @@ public abstract class AnnotationUtils {
9191 * Get a single {@link Annotation} of {@code annotationType} from the supplied
9292 * annotation: either the given annotation itself or a direct meta-annotation
9393 * 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.
9697 * @param ann the Annotation to check
9798 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
9899 * @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
115116
116117 /**
117118 * 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.
121124 * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation
122125 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
123126 * @return the matching annotation, or {@code null} if not found
@@ -144,10 +147,13 @@ public static <T extends Annotation> T getAnnotation(AnnotatedElement annotatedE
144147 }
145148
146149 /**
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}.
148153 * <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.
151157 * @param method the method to look for annotations on
152158 * @param annotationType the annotation type to look for
153159 * @return the matching annotation, or {@code null} if not found
@@ -256,34 +262,100 @@ public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedEle
256262 }
257263
258264 /**
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
260324 * {@link Method}, traversing its super methods (i.e., from superclasses and
261325 * 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.
262329 * <p>Annotations on methods are not inherited by default, so we need to handle
263330 * this explicitly.
264- * <p>Meta-annotations will <em>not</em> be searched.
265331 * @param method the method to look for annotations on
266332 * @param annotationType the annotation type to look for
267333 * @return the matching annotation, or {@code null} if not found
334+ * @see #getAnnotation(Method, Class)
268335 */
269336 @ SuppressWarnings ("unchecked" )
270337 public static <A extends Annotation > A findAnnotation (Method method , Class <A > annotationType ) {
271338 AnnotationCacheKey cacheKey = new AnnotationCacheKey (method , annotationType );
272339 A result = (A ) findAnnotationCache .get (cacheKey );
340+
273341 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+
276345 if (result == null ) {
277- result = searchOnInterfaces (method , annotationType , clazz .getInterfaces ());
346+ result = searchOnInterfaces (method , annotationType , method . getDeclaringClass () .getInterfaces ());
278347 }
348+
349+ Class <?> clazz = method .getDeclaringClass ();
279350 while (result == null ) {
280351 clazz = clazz .getSuperclass ();
281352 if (clazz == null || clazz .equals (Object .class )) {
282353 break ;
283354 }
284355 try {
285356 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 );
287359 }
288360 catch (NoSuchMethodException ex ) {
289361 // No equivalent method found
@@ -292,9 +364,10 @@ public static <A extends Annotation> A findAnnotation(Method method, Class<A> an
292364 result = searchOnInterfaces (method , annotationType , clazz .getInterfaces ());
293365 }
294366 }
295- if (result != null ) {
296- findAnnotationCache .put (cacheKey , result );
297- }
367+ }
368+
369+ if (result != null ) {
370+ findAnnotationCache .put (cacheKey , result );
298371 }
299372 return result ;
300373 }
@@ -680,7 +753,7 @@ else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
680753 }
681754
682755 /**
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
684757 * single-element Annotation, given an annotation instance.
685758 * @param annotation the annotation instance from which to retrieve the value
686759 * @return the attribute value, or {@code null} if not found
@@ -712,7 +785,7 @@ public static Object getValue(Annotation annotation, String attributeName) {
712785 }
713786
714787 /**
715- * Retrieve the <em>default value</em> of the {@code " value" } attribute
788+ * Retrieve the <em>default value</em> of the {@code value} attribute
716789 * of a single-element Annotation, given an annotation instance.
717790 * @param annotation the annotation instance from which to retrieve the default value
718791 * @return the default value, or {@code null} if not found
@@ -737,7 +810,7 @@ public static Object getDefaultValue(Annotation annotation, String attributeName
737810 }
738811
739812 /**
740- * Retrieve the <em>default value</em> of the {@code " value" } attribute
813+ * Retrieve the <em>default value</em> of the {@code value} attribute
741814 * of a single-element Annotation, given the {@link Class annotation type}.
742815 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved
743816 * @return the default value, or {@code null} if not found
0 commit comments