|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2013 the original author or authors. |
| 2 | + * Copyright 2002-2014 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.
|
@@ -240,52 +240,68 @@ private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
|
240 | 240 | }
|
241 | 241 |
|
242 | 242 | /**
|
243 |
| - * Find a single {@link Annotation} of {@code annotationType} from the supplied |
244 |
| - * {@link Class}, traversing its annotations, interfaces, and superclasses if |
245 |
| - * no annotation can be found on the given class itself. |
| 243 | + * Find a single {@link Annotation} of {@code annotationType} on the |
| 244 | + * supplied {@link Class}, traversing its interfaces, annotations, and |
| 245 | + * superclasses if the annotation is not <em>present</em> on the given class |
| 246 | + * itself. |
246 | 247 | * <p>This method explicitly handles class-level annotations which are not
|
247 |
| - * declared as {@link java.lang.annotation.Inherited inherited} <i>as well |
248 |
| - * as meta-annotations and annotations on interfaces</i>. |
| 248 | + * declared as {@link java.lang.annotation.Inherited inherited} <em>as well |
| 249 | + * as meta-annotations and annotations on interfaces</em>. |
249 | 250 | * <p>The algorithm operates as follows:
|
250 | 251 | * <ol>
|
251 |
| - * <li>Search for an annotation on the given class and return it if found. |
252 |
| - * <li>Recursively search through all interfaces that the given class |
253 |
| - * declares, returning the annotation from the first matching candidate, if any. |
254 |
| - * <li>Recursively search through all annotations that the given class |
255 |
| - * declares, returning the annotation from the first matching candidate, if any. |
256 |
| - * <li>Proceed with introspection of the superclass hierarchy of the given |
257 |
| - * class by returning to step #1 with the superclass as the class to look for |
258 |
| - * annotations on. |
| 252 | + * <li>Search for the annotation on the given class and return it if found. |
| 253 | + * <li>Recursively search through all interfaces that the given class declares. |
| 254 | + * <li>Recursively search through all annotations that the given class declares. |
| 255 | + * <li>Recursively search through the superclass hierarchy of the given class. |
259 | 256 | * </ol>
|
| 257 | + * <p>Note: in this context, the term <em>recursively</em> means that the search |
| 258 | + * process continues by returning to step #1 with the current interface, |
| 259 | + * annotation, or superclass as the class to look for annotations on. |
260 | 260 | * @param clazz the class to look for annotations on
|
261 |
| - * @param annotationType the annotation class to look for |
262 |
| - * @return the annotation found, or {@code null} if none found |
| 261 | + * @param annotationType the type of annotation to look for |
| 262 | + * @return the annotation if found, or {@code null} if not found |
263 | 263 | */
|
264 | 264 | public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
|
| 265 | + return findAnnotation(clazz, annotationType, new HashSet<Annotation>()); |
| 266 | + } |
| 267 | + |
| 268 | + /** |
| 269 | + * Perform the search algorithm for {@link #findAnnotation(Class, Class)}, |
| 270 | + * avoiding endless recursion by tracking which annotations have already |
| 271 | + * been visited. |
| 272 | + * @param clazz the class to look for annotations on |
| 273 | + * @param annotationType the type of annotation to look for |
| 274 | + * @param visitedAnnotations the set of annotations that have already been visited |
| 275 | + * @return the annotation if found, or {@code null} if not found |
| 276 | + */ |
| 277 | + private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, |
| 278 | + Set<Annotation> visitedAnnotations) { |
265 | 279 | Assert.notNull(clazz, "Class must not be null");
|
| 280 | + |
266 | 281 | A annotation = clazz.getAnnotation(annotationType);
|
267 | 282 | if (annotation != null) {
|
268 | 283 | return annotation;
|
269 | 284 | }
|
270 | 285 | for (Class<?> ifc : clazz.getInterfaces()) {
|
271 |
| - annotation = findAnnotation(ifc, annotationType); |
| 286 | + annotation = findAnnotation(ifc, annotationType, visitedAnnotations); |
272 | 287 | if (annotation != null) {
|
273 | 288 | return annotation;
|
274 | 289 | }
|
275 | 290 | }
|
276 |
| - if (!Annotation.class.isAssignableFrom(clazz)) { |
277 |
| - for (Annotation ann : clazz.getAnnotations()) { |
278 |
| - annotation = findAnnotation(ann.annotationType(), annotationType); |
| 291 | + for (Annotation ann : clazz.getAnnotations()) { |
| 292 | + if (!visitedAnnotations.contains(ann)) { |
| 293 | + visitedAnnotations.add(ann); |
| 294 | + annotation = findAnnotation(ann.annotationType(), annotationType, visitedAnnotations); |
279 | 295 | if (annotation != null) {
|
280 | 296 | return annotation;
|
281 | 297 | }
|
282 | 298 | }
|
283 | 299 | }
|
284 |
| - Class<?> superClass = clazz.getSuperclass(); |
285 |
| - if (superClass == null || superClass.equals(Object.class)) { |
| 300 | + Class<?> superclass = clazz.getSuperclass(); |
| 301 | + if (superclass == null || superclass.equals(Object.class)) { |
286 | 302 | return null;
|
287 | 303 | }
|
288 |
| - return findAnnotation(superClass, annotationType); |
| 304 | + return findAnnotation(superclass, annotationType, visitedAnnotations); |
289 | 305 | }
|
290 | 306 |
|
291 | 307 | /**
|
|
0 commit comments