Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Documenting Spring @RequestMapping annotations into one location automatically?

Javadoc is great for scanning all of source files and creating HTML pages to view it. I was wondering if there is a similar tool that would go through all of your Spring controllers and collect all of the methods that have been annotated with @RequestMapping and produce a single HTML page listing them. Sort of like a pseudo site map for developers to ensure uniqueness and standardization across controllers.

I apologize if this question has been asked elsewhere already. I could not come up with an appropriate set of search terms that would provide a useful result.

like image 959
LostHisMind Avatar asked Jan 28 '11 15:01

LostHisMind


People also ask

Which annotation is used with @RequestMapping to bind a web request?

The @RequestParam annotation is used with @RequestMapping to bind a web request parameter to the parameter of the handler method.

What is true about @RequestMapping annotation in Spring MVC?

The @RequestMapping annotation can be applied to class-level and/or method-level in a controller. The class-level annotation maps a specific request path or pattern onto a controller. You can then apply additional method-level annotations to make mappings more specific to handler methods.

What is the difference between GetMapping and RequestMapping?

@RequestMapping is used at the class level while @GetMapping is used to connect the methods. This is also an important Spring MVC interview question to knowing how and when to use both RequestMapping and GetMapping is crucial for Java developers.

Which of the following is the method where RequestMapping is used with fallback method?

@RequestMapping fallback method: We can create a fallback method for the controller class to make sure we are catching all the client requests even though there are no matching handler methods. It is useful in sending custom 404 response pages to users when there are no handler methods for the request.


1 Answers

This is a very good question, I often miss (and implement) functionality like this.

Use a Build Tool

What I'd do is run Maven (or ant) and execute a task after compilation that

  • reads all classes (perhaps with a configurable list of packages)
  • iterates over all methods of these classes
  • reads the annotations
  • and writes the output to HTML

Use Annotation Processing

But I guess this is a scenario, where annotation processing might also be a way to do it. Usually, you have to use some internal APIs to get stuff done in API, but using Filer.createResource(...) it should actually possible to do it out of the box.

Here's a rudimentary implementation:

public class RequestMappingProcessor extends AbstractProcessor{

    private final Map<String, String> map =
        new TreeMap<String, String>();

    private Filer filer;

    @Override
    public Set<String> getSupportedAnnotationTypes(){
        return Collections.singleton(RequestMapping.class.getName());
    }

    @Override
    public synchronized void init(
        final ProcessingEnvironment processingEnv){
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(
        final Set<? extends TypeElement> annotations,
        final RoundEnvironment roundEnv){

        for(final TypeElement annotatedElement : annotations){
            final RequestMapping mapping =
                annotatedElement.getAnnotation(
                    RequestMapping.class
                );
            if(mapping != null){
                addMapping(mapping, annotatedElement, roundEnv);
            }
        }
        assembleSiteMap();
        return false;
    }

    private void assembleSiteMap(){
        Writer writer = null;
        boolean threw = false;
        try{
            final FileObject fileObject =
                filer.createResource(
                    StandardLocation.CLASS_OUTPUT,
                    "html", "siteMap.html"
                );
            writer = fileObject.openWriter();
            writer.append("<body>\n");
            for(final Entry<String, String> entry : map.entrySet()){
                writer
                    .append("<a href=\"")
                    .append(entry.getKey())
                    .append("\">")
                    .append("Path: ")
                    .append(entry.getKey())
                    .append(", method: ")
                    .append(entry.getValue())
                    .append("</a>\n");
            }
            writer.append("</body>\n");

        } catch(final IOException e){
            threw = true;
            throw new IllegalStateException(e);
        } finally{

            // with commons/io: IOUtils.closeQuietly(writer)
            // with Guava: Closeables.close(writer, rethrow)
            // with plain Java this monstrosity:
            try{
                if(writer != null){
                    writer.close();
                }
            } catch(final IOException e){
                if(!threw){
                    throw new IllegalStateException(e);
                }
            } finally{
            }
        }
    }

    private void addMapping(final RequestMapping mapping,
        final TypeElement annotatedElement,
        final RoundEnvironment roundEnv){
        final String[] values = mapping.value();
        for(final String value : values){
            map.put(
                value,
                annotatedElement.getQualifiedName().toString()
            );
        }
    }

}
like image 55
Sean Patrick Floyd Avatar answered Oct 07 '22 18:10

Sean Patrick Floyd