code: @SuppressWarnings("unchecked")
  	private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
  		Assert.notNull(clazz, "Class must not be null");
  
  		try {
  			Annotation[] anns = clazz.getDeclaredAnnotations();
  			for (Annotation ann : anns) {
  				if (ann.annotationType().equals(annotationType)) {
  					return (A) ann;
  				}
  			}
  			for (Annotation ann : anns) {
  				if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
  					A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
  					if (annotation != null) {
  						return annotation;
  					}
  				}
  			}
  		}
  		catch (Exception ex) {
  			// Assuming nested Class values not resolvable within annotation attributes...
  			logIntrospectionFailure(clazz, ex);
  			return null;
  		}
  
  		for (Class<?> ifc : clazz.getInterfaces()) {
  			A annotation = findAnnotation(ifc, annotationType, visited);
  			if (annotation != null) {
  				return annotation;
  			}
  		}
  
  		Class<?> superclass = clazz.getSuperclass();
  		if (superclass == null || superclass.equals(Object.class)) {
  			return null;
  		}
  		return findAnnotation(superclass, annotationType, visited);
  	}
docs: Perform the search algorithm for {@link #findAnnotation(Class, Class)},
  avoiding endless recursion by tracking which annotations have already
  been <em>visited</em>.
  @param clazz the class to look for annotations on
  @param annotationType the type of annotation to look for
  @param visited the set of annotations that have already been visited
  @return the annotation if found, or {@code null} if not found