Search code examples
javaspringspring-mvcreflectioncglib

Can't see annotations via reflection despite RetentionPolicy being RUNTIME


I'm trying to find methods in a Spring RestController that are annotated with a given annotation. In order to see what annotations exist on methods for that RestController, I've done the following:

Map<String, Object> beans = appContext.getBeansWithAnnotation(RestController.class);
for (Map.Entry<String, Object> entry : beans.entrySet()) {
    Method[] allMethods = entry.getValue().getClass().getDeclaredMethods();
    for(Method method : allMethods) {
        LOG.debug("Method: " + method.getName());
        Annotation[] annotations = method.getDeclaredAnnotations();
        for(Annotation annotation : annotations) {
            LOG.debug("Annotation: " + annotation);
        }
    }
}

The problem is that I'm not seeing any annotations at all, despite the fact that I know I have at least one that is annotated with @Retention(RetentionPolicy.RUNTIME). Any ideas? Is CGLIB a factor here? (Being a Controller, the methods in question are bieing proxied using CGBLIB).


Solution

  • Due to the @PreAuthorize annotation you don't get the actual class but a proxied instance of that class. As annotations aren't inherited (by design in the language) you won't see them.

    I suggest to do 2 things, first use the AopProxyUtils.ultimateTargetClass to get the actual class of the bean and second use the AnnotationUtils to get the annotation from the class.

    Map<String, Object> beans = appContext.getBeansWithAnnotation(RestController.class);
    for (Map.Entry<String, Object> entry : beans.entrySet()) {
        Class clazz = AopProxyUtils. AopProxyUtils.ultimateTargetClass(entry.getValue());
        ReflectionUtils.doWithMethods(clazz, new MethodCallback() {
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                Annotation[] annotations = AnnotationUtils.getAnnotations(method);
                for(Annotation annotation : annotations) {
                    LOG.debug("Annotation: " + annotation);
                }
            }
        });
    }
    

    Something like that should do the trick, also some cleanup using the Spring provided utility classes.