2

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?

Community
  • 1
  • 1
Naman
  • 27,789
  • 26
  • 218
  • 353
  • The one thing I dont get: how/where do you create **instances** of your controller objects? Your methods arent static, so on "which" objects do you invoke them? – GhostCat Sep 07 '16 at 04:28
  • How does the method being static/non-static relate to reflection ? @GhostCat – Naman Sep 07 '16 at 04:52
  • As said: knowing *classes* and *methods* is one thing. But if you want to use **reflection** to invoke methods, too ... then you need to know the **object** to invoke on, too. Just the javadoc for Method.invoke()! – GhostCat Sep 07 '16 at 04:54
  • @GhostCat In my case I am not bothered of calling the `invoke()` anyway, the motive is not to use any functionality but to get the mappings as of now. – Naman Sep 07 '16 at 04:58
  • Ok, one thing: as you didnt comment on my answer - does it help you in any way; or is it missing something you need? – GhostCat Sep 07 '16 at 06:42
  • @GhostCat - the reason I didn't comment yet is, coz its more of ideal concepts, which I am currently trying to implement right now. Once completed would confirm :) – Naman Sep 07 '16 at 06:59
  • Fine; let me know if there is anything i can help with/or that would improve the answer. – GhostCat Sep 07 '16 at 07:03

1 Answers1

2

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.

Naman
  • 27,789
  • 26
  • 218
  • 353
GhostCat
  • 137,827
  • 25
  • 176
  • 248