开发者

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 si开发者_运维百科milar 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.


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()
            );
        }
    }

}


There's nothing that I know of that would do that. I've retrieved controllers and mappings via the app context before to create navigation, but it was a lot of work for little gain IMO:

@Component
public class SiteMap implements ApplicationContextAware, InitializingBean {

  private ApplicationContext context;
  private List<Page> pages = new ArrayList<Page>();

  public List<Page> getPages() {
     return pages;
  }

  public void setApplicationContext(ApplicationContext applicationContext) {
     this.context = applicationContext;
  }

  public void afterPropertiesSet() throws Exception {
     Assert.notNull(context, "applicationContext not set");
     Map<String, Object> controllers = ctx.getBeansWithAnnotation(Controller.class);
     for(Map.Entry<String, Object> entry : controllers.entrySet()) {
        Page page = new Page();
        Class<?> controllerClass = entry.getValue();
        String controllerRoot = null;
        RequestMapping classMapping = controllerClass.getAnnotation(RequestMapping.class);
        if(classMapping != null)
            controllerRoot = classMapping.value();
        if(controllerRoot = null)
            controllerRoot = // get and parse controller name
        page.setPath(controllerRoot);
        for(Method m : controllerClass.getDeclaredMethods()) {
            RequestMapping rm = m.getAnnotation(RequestMapping.class);
            if(rm == null)
               continue;
            Page child = new Page();
            child.setPath(rm.value());
            page.getChildren().add(child);
        }
        pages.add(page);
     }
  }

  public static class Page {
      private String path;
      private List<Page> children = new ArrayList<Page>();
      // other junk
  }

}

Then access ${pages} in your site map page JSP. Might need to play with that code some if you do something similar, I freehanded it in this editor.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜