Documenting Spring @RequestMapping annotations into one location automatically?

℡╲_俬逩灬. 提交于 2019-12-22 05:27:19

问题


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.


回答1:


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

}



回答2:


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.



来源:https://stackoverflow.com/questions/4829774/documenting-spring-requestmapping-annotations-into-one-location-automatically

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!