前一篇介绍了 JavaDoc 编程,书写自定义的 Taglet 支持 @unmi 等, 那时提到了 Doclet,但是差点无视了 Doclet,现在才知道 Doclet 真是太强大了,有了它你会觉得 javadoc 已经不是原本的那个 javadoc 了,别再把 javadoc 看作只会生成一大堆 HTML 文件的工具了。尤其是搭配上自定义的 Taglet,那可是,自已体验吧。
Doclet 是 JavaDoc 的一个很隆重的扩展点,可以在执行 javadoc 时用 -doclet 来指定自己的 Doclet,那么 doclet 可以为我们做些什么呢?
可以为我们生成 HTML 的 JavaDoc API 文档,这就是默认的 com.sun.tools.doclets.standard.Standard 为我们做的事,还可以像以前那样从源文件中抽取信息生成各种 XML 文件,或是 PDF, Excel, UML 图等等任何可能的内容,或做任何有作为的事情。总之在 doclet 中可以感知道对任何包,类,方法,字段等的遍历。这里 Doclet.com 有大量的第三方的 doclet 供你选择,如:
AntDoclet, API Guide Doclet, EJBGen, Java2Rose Doclet, JDiff, JUnitDoclet, LaTeXtaglet, PDFDoclet, PublishedApiDoclet, ServletDoclet, Spell Check Doclet, UMLGraph, VelocityDoclet, XDoclet, xml-doclet 等数十种 Doclet, 还可以找到别的,所以你要是不想自定义 Doclet 的话,有第三方可用就直接用人家的就行。
总有特殊需求的时候,总有要自定义 Doclet 的时候。说那么多总要来看看 Doclet 可以用来做什么,见如下 DocumentDoclet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
package cc.unmi; import com.sun.javadoc.ClassDoc; import com.sun.javadoc.DocErrorReporter; import com.sun.javadoc.Doclet; import com.sun.javadoc.MethodDoc; import com.sun.javadoc.RootDoc; import com.sun.javadoc.Tag; import com.sun.tools.doclets.formats.html.HtmlDoclet; import com.sun.tools.doclets.standard.Standard; /** * Customized Doclet DocumentDoclet * @author Unmi * */ public class DocumentDoclet extends Doclet{ public static boolean start(RootDoc root) { doityouself(root.classes()); return true; //return HtmlDoclet.start(root); } public static boolean validOptions(String[][] paramArrayOfString, DocErrorReporter paramDocErrorReporter) { System.out.println("Print arguments:"); for (String[] strings : paramArrayOfString) { System.out.println(" "+strings[0] + "->" + strings[1]); } System.out.println(); return Standard.validOptions(paramArrayOfString, paramDocErrorReporter); } private static void doityouself(ClassDoc[] classes) { for (int i = 0; i < classes.length; i++) { MethodDoc[] methods = classes[i].methods(); for (int j = 0; j < methods.length; j++) { //add comments methods[j].setRawCommentText("Add method comment text\n" + methods[j].getRawCommentText() + "\n@document new document annotation"); Tag[] tags = methods[j].tags("myTag"); if (tags.length > 0) { System.out.println("\n" + classes[i].name() + ", Comment: " + classes[i].commentText()); System.out.println(" " + methods[j].name()); for (int k = 0; k < tags.length; k++) { System.out.println(" " + tags[k].name() + ": " + tags[k].text()); } } Tag[] documentTags = methods[j].tags("document"); if (tags.length > 0) { System.out.println("\n@document: " + documentTags[0].text()); } } } } } |
你可以让你的 Doclet 继承自 com.sun.javadoc.Doclet,也未强求,关键是要写上前面的 start(RootDoc) 方法,JavaDoc 会调用这个方法,此方法中可以获取到所有的与文档注释相关的信息。在 validOptions 方法中可对注释信息进行校验,其中可以得到传入的所有参数信息。
因为它用到了 tools.jar 中的类,所以编译的时候需要引入 JDK 目录中的 tools.jar 包。
用于测试的代码 TestJavaDoclet.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * Test class for DocumentDoclet * @author Unmi */ public class TestJavaDoclet { /** * Main method of TestJavaDoclet * @myTag myTag value */ public static void main(String[] args) { // TODO Auto-generated method stub } } |
假设编译出的 DocumentDoclet 生成在 c:/classes/cc/unmi/DocumentDoclet.class,现在命令行进到 TestJavaDoclet.java 所在的目录执行:
javadoc -doclet cc.unmi.DocumentDoclet -docletpath c:/classes *.java
控制台会输出:
-doclet->cc.unmi.DocumentDoclet
-docletpath->bin
-sourcepath->srcLoading source file src/TestJavaDoclet.java...
Constructing Javadoc information...TestJavaDoclet, Comment: Test class for DocumentDoclet
main
@myTag: myTag value@document: new document annotation
从上面的输出我们可以看到自己的 DocumentDoclet 知道 javadoc 命令传入的参数,也可获知每一个包,类,方法,字段的注释信息,并且可以通过 setRawCommentText() 方法来动态的修改注释信息。同时你应该找不到 javadoc api 的 HTML 帮助文件了,对了,因为它们根本就没有生成,这似乎不像原来的 javadoc 了。
这就是 Doclet 的价值所在,不再纯粹的生成 javadoc api 文档,你可把它挪作他用。这个 Doclet 为什么没有生成那些 HTML 文件呢?因为其实生成 HTML 文件是由默认的 com.sun.tools.doclets.standard.Standard 干的,而今用了自己的 DocumentDoclet,故而不在生成那些 HTML 文件了。
谁曾说过 javadoc 命令就是用来生成 JavaDoc API 的 HTML 参考文档的呢?
如果仍想那些 HTML 文件照样的产生出来,可以在 DocumentDoclet.start(Rootdoc root) 方法中不直接 return true, 而是
return HtmlDoclet.start(root) 或
return Standard.start(root) 都可以。
经历过了上面的代码的理解,我们就可以在 start(RootDoc root) 中做更多的事情了,收集源文件中的注释信息生成任何你想要的文档格式文件都行。用了上面其中的一个 return 方法,再看下生成的 API 文档中的显示:
在 DocumentDoclet 中新加的注释信息也生成到了 API 文档中了。
接着再来看下自定义的 DocumentDoclet 与 Taglet 怎么结果使用,比如前面在 DocumentDoclet 中加上的 @document new document annotation 内容并未出现在 API 文档中,那我们试着用命令:
javadoc -doclet cc.unmi.DocumentDoclet -docletpath c:/classes -tag document:a:Document *.java
提示错误: javadoc: error - invalid flag: -tag , 此路不通!
因为用 javadoc -help 命令看下就发现 -tag 参数是 Provided by Standard doclet: , 由 com.sun.tools.doclets.standard.Standard这个默认的 Doclet 提供了。
于是我们让前面的 DocumentDoclet 继承自 com.sun.tools.doclets.standard.Standard 后,再执行前面的命令,正常工作,并且新加的 @document 的内容反映在了 API 文档中了:
再进一步深入,如果我们使用前文 JavaDoc 编程,书写自定义的 Taglet 支持 @unmi 等 的 DocumentTaglet 的话,需要执行命令:
javadoc -doclet cc.unmi.DocumentDoclet -docletpath c:/classes -taglet cc.unmi.DocumentTaglet -tagletpath c:/classes *.java
见红了,也就说明了前面自定义的 DocumentTaglet 应用上了,注意的是我们的 DocumentDoclet 仍然需要继承自 Standard 类,否则还是会报 -taglet 参数无效。
再加几个说明:
1. DocumentDoclet 比 DocumentTaglet 要更早执行
2. DocumentDoclet 中可以获取到执行 javadoc 命令时所携带的参数,这好像 DocumentTaglet 做不到的
3. 虽然这里的 DocumentDoclet 和 DocumentTaglet 是由 javadoc 发起来同一个 JVM 中执行,但它们却由不同的类加载器加载的,这两类加载器有共同的父类。所以它们两的类实例是不同的,具体表现就是在 DocumentDoclet 中对某个类(如 Helper) 的静态属性所赋的值对于 DocumentTaglet 是不可见的。除非都把 Helper 类委托给他们的父类加载器加载,虽把 Helper 放在父类加载器的 classpath 下。
参考: 1. Code generation using Javadoc
2. Doclet Overview
3. Doclava: Custom Javadoc Doclet from Google
4. Create a Taglet to document database access (Javadoc)
本文链接 https://yanbin.blog/javadoc-customize-doclet/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
你好,请问doclet源码可以提供一下么?谢谢,找了很多都是oracle简化版没有实际代码的框架。
文中包含所有代码,是否代码无法运行?
可以运行