JavaDoc 编程,书写自定义的 Doclet, 定制输出

前一篇介绍了 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:

你可以让你的 Doclet 继承自 com.sun.javadoc.Doclet,也未强求,关键是要写上前面的 start(RootDoc) 方法,JavaDoc 会调用这个方法,此方法中可以获取到所有的与文档注释相关的信息。在 validOptions 方法中可对注释信息进行校验,其中可以得到传入的所有参数信息。

因为它用到了  tools.jar 中的类,所以编译的时候需要引入 JDK 目录中的 tools.jar 包。

用于测试的代码 TestJavaDoclet.java:

假设编译出的  DocumentDoclet 生成在 c:/classes/cc/unmi/DocumentDoclet.class,现在命令行进到 TestJavaDoclet.java 所在的目录执行:

javadoc -doclet cc.unmi.DocumentDoclet -docletpath c:/classes *.java

控制台会输出:

Print arguments:
  -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 文档中的显示:

custom_doclet_1

在 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 文档中了:

custom_doclet_2

再进一步深入,如果我们使用前文 JavaDoc 编程,书写自定义的 Taglet 支持 @unmi 等 的 DocumentTaglet 的话,需要执行命令:

javadoc -doclet cc.unmi.DocumentDoclet -docletpath c:/classes -taglet cc.unmi.DocumentTaglet -tagletpath c:/classes *.java

生成的 API 文档就成了:custom_doclet_3

见红了,也就说明了前面自定义的 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

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

3 Comments
Inline Feedbacks
View all comments
胡静桃
胡静桃
6 years ago

你好,请问doclet源码可以提供一下么?谢谢,找了很多都是oracle简化版没有实际代码的框架。

Yanbin
6 years ago
Reply to  胡静桃

文中包含所有代码,是否代码无法运行?

测试人员
测试人员
4 years ago
Reply to  Yanbin

可以运行