Naman
Naman

Reputation: 31858

How to use reflection to identify annotated methods?

We are trying to use reflection to extract the exact mapped values to our methods. The code structure in the target module consists of multiple classes as follows -

@Controller
@RequestMapping(value = "/questions")
public class XYZController {

  @RequestMapping(value = "/ask")
  public boolean myMethod1() {..}

  @RequestMapping(value = "/{questionNo.}/{questionTitle}")
  public MyReturnObject myMethod2(){..}

}

What we are trying to grep here is the list of endpoints like /questions/ask and /questions/{questionNo.}/{questionTitle} for which the code we tried to execute filters all such classes based on the @Controller annotation and at the same time we are able to get the list of all the endpoints separately. The code we have tried so far is -

public class ReflectApi {

    public static void main(String[] args) {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true);
        final List<String> filteredClasses = new ArrayList<>();
        scanner.addIncludeFilter(new AnnotationTypeFilter(Controller.class));
        Set<BeanDefinition> filteredPackage = scanner.findCandidateComponents("com.package.test");

        // gives me a list of all the filtered classes
        filteredPackage.stream().forEach(beanDefinition -> filteredClasses.add(beanDefinition.getBeanClassName()));

        // call to get the annotation details
        filteredClasses.stream().forEach(filteredClass -> getEndPoints(filteredClass));
    }

    public static void getEndPoints(String controllerClassName) {
        try {
            Class clazz = Class.forName(controllerClassName);
            Annotation classAnnotation = clazz.getDeclaredAnnotation(RequestMapping.class);
            if (classAnnotation != null) {
                RequestMapping mappedValue = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
                System.out.println("Controller Mapped -> " + mappedValue.value()[0]); //This gives me the value for class like "/questions"
                runAllAnnotatedWith(RequestMapping.class);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // followed from a SO reference
    public static void runAllAnnotatedWith(Class<? extends Annotation> annotation) {
        Reflections reflections = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forJavaClassPath())
                .setScanners(new MethodAnnotationsScanner()));
        Set<Method> methods = reflections.getMethodsAnnotatedWith(annotation);

        methods.stream().forEach(m -> {
            if (m != null) {
                RequestMapping mappedValue = m.getAnnotation(RequestMapping.class); //this is listing out all the @RequestMapping annotations like "/questions" , "/ask" etc.
                    System.out.println(mappedValue.value()[0]);
            }
        });
    }
}

But the missing part is the concatenation of the class to method RequestMapping value here.

How do we loop inside a class to search annotations only from its methods?

Or is there any simpler way of doing this from what we are using?

References used - Scanning Java annotations at runtime && How to run all methods with a given annotation?

Upvotes: 2

Views: 1611

Answers (1)

GhostCat
GhostCat

Reputation: 140417

Aren't endpoints methods in the end?

So, I think what you need to implement is:

  1. => Retrieve all declared methods of each and any class that has @RequestMapping and @Controller annotations.

  2. => Iterate the annotations on each of those Method objects to check if they contain @RequestMapping

OR

  1. => Iterate through all the Methods for each class in Step 1 and find the annotated one's using method.getDeclaredAnnotation(RequestMapping.class) and skip Step 3

  2. => Remember that combination of class and method for your mapping

  3. => Do some error handling, in case you don't find any annotated method in there.

Upvotes: 2

Related Questions