关键知识提要
往往我们生成api文档都是使用swagger这种(点击查看我很喜欢的一个作者写的教程),具有一定的代码入侵性,你需要在各种变量上标识各种注解,这就是一种很额外的工作。相对于手动加注解,如果能够通过我们平常都在写的javadoc格式的注释生成文档就好了,就和javadoc指令生成的一样,甚至还允许自定义输出格式就更好了。
这样在写注释的同时也同时做好了api文档
关于javadoc
首先javadoc是jdk内置的一种从源代码里面抽离注释并生成文档的工具。
doclet参数
然后我们再认识一个javadoc命令的参数 -doclet 。这个参数后面跟随的类名允许你将自定义javadoc的输出到这个类中,获取RootDoc对象。
使用自定义doclet
当我们利用com.sun.tools.javadoc.Main.execute执行javdoc指令时会读取-doclet后的类名,在这里就是JavaDocLetUtil的全限名,寻找里面方法签名为public static boolean start(RootDoc rootDoc)的函数,将RootDoc传入,这里我就做了一步缓存这个RootDoc对象。
public class JavaDocLetUtil {
private static RootDoc rootDoc;
private JavaDocLetUtil(){}
public static boolean start(RootDoc rootDoc){
JavaDocLetUtil.rootDoc = rootDoc;
return true;
}
private static void execute(String[] args){
com.sun.tools.javadoc.Main.execute(args);
}
private static RootDoc getRootDoc(){
return Optional.ofNullable(rootDoc).orElseThrow(()->new RuntimeException("未初始化"));
}
public static RootDoc generateControllersRootDoc(String controllerPagePath){
List<String> args = new ArrayList<>();
args.add("-doclet");
args.add(JavaDocLetUtil.class.getName());
final ArrayList<String> filePath = new ArrayList<>();
//省略向filePath添加文件路径的步骤
args.addAll(filePath);
execute(args.toArray(new String[args.size()]));
return JavaDocLetUtil.getRootDoc();
}
}
这个代码执行逻辑为
JavaDocLetUtil.generateControllersRootDoc() 传入文件地址
拼接指令后交由execute执行,最后getRootDoc()获取此次RootDoc对象
关于RootDoc对象
这里会涉及到这么几个对象RootDoc,ClassDoc,MethodDoc
其中的关系为RootDoc包含ClassDoc(个数取决于你传入了多少个类文件地址),ClassDoc包含MethodDoc(取决于该类有多少个方法)
示意图如下
关于ClassDoc接口
继承关系如下
我们关注几个重点方法
1,MethodDoc[] methods()
从方法签名我们就可以得知这个方法用于返回该类下所有MethodDoc对象
2,String qualifiedName() (继承自ProgramElementDoc接口)
用于返回全限名,所以我们可以通过Class.forName(classDoc.qualifiedName())获取到对应的Class对象
3,String commentText();
Tag[] tags();
String commentText(); (继承自Doc接口)
用于返回我们的注释内容,这个就是实现既要无侵入的又要给予信息描述的关键
关于MethodDoc接口
继承关系如下
我们关注几个重点方法
1,String name(); (继承自Doc类)
获取到方法名,比如 public void fun(){}获取到的就是字符串:“fun”
2,Parameter[] parameters(); (继承自ExecutableMemberDoc)
返回的Parameter全限定名为com.sun.javadoc.Paramter,这个方法用于获取参数信息
其中Parameter中Type type()这个方法的返回值Type(全限定名为com.sun.javadoc.Type)中有个方法String qualifiedTypeName()获取这个参数类型全限定名,进而利用Class.forName获取对应Class对象
再结合我们上面提到的从ClassDoc中获取的Class对象,通过Class类提供的getMethod方法,利用name()获取函数名,在如上得到参数的Class对象就能获取到对应的Method对象了
3,Tag[] tags()
Tag[] tags(String tagname)
String commentText() (继承自Doc接口)
其中Tag类中String text()会返回这个标签的注释信息
举个例子
若Tag为 “@param list 批量删除的id集合”,text则会返回“list 批量删除的id集合”
总结
一般来讲一个模块就是一个Controller类,通过该类的注释进行描述,再获取到对应的Class对象就够获取到诸如@RequestMapping之类的注解就能获取到路径了
然后获取的方法参数和注释一一对应就能获取到对该参数的描述信息,同时获取的Method对象也能得到诸如@RequestMapping获取到uri信息,获取到参数的@ResponseBody,@RequestHeader注解进而为参数添加额外描述