javadoc 可为我们的 Java 项目生成 API 文档,别人的应该是看得多了,自己的可能不好意思晾出来看。那 Java 源代码里的 @author, @see, @param 等应该是司空见惯了吧。除此之外我们还可以自定义自己的 tag,并让它们的内容按照我们需要的格式生成到 javadoc 文档中,或作他用。还记得没有 Maven 的时代我们是怎样用 XDoclet 生映射文件的吗?现在的 Taglet 定制想要做的事情大抵如此。
执行一下 javadoc 命令看看,一堆的参数可以指定,又有学问在里头,且看:
-tag <name>:<locations>:<header> Specify single argument custom tags
-taglet The fully qualified name of Taglet to register
-tagletpath The path to Taglets
和
-doclet <class> Generate output via alternate doclet
-docletpath <path> Specify where to find doclet class files
关于 doclet 部份这儿暂且不说,单讲 tag 部分的东西。
对于自定义 tag,简单的时候,用参数 -tag 都可以不写自己的 taglet 类,例如有这样一个代码:
1 2 3 4 5 6 7 8 9 10 11 |
public class TestJavaDocTag { /** * @param args input command arguments * @document help * yourself */ public static void main(String[] args) { } } |
上面使用了 @document 自定义 tag,要为它生成文档,可以用命令:
javadoc -tag document:a:Document: *.java
于是生成的 javadoc 文档中有了:
-tag 参数的 name, header 部分一对照就知道了,中间那个 location 参数代表修饰谁的注释要被解析,取值有:
X
(disable tag)
a
(all)
o
(overview)
p
(packages)
t
(types, that is classes and interfaces)
c
(constructors)
m
(methods)
f
(fields)
上面的 -tag 为 @document 生成的 HTML 是:
1 2 3 |
<b>Document:</b></dt> <dd>help yourself</dd> |
如果我们要为前面的 @document 生成更具表现力说明,或是另有企图 -- 如提取 @document 后的内容生成自己的外部文件中,那现在就得让 Taglet 登场了。从 DocumentTaglet 代码开始,它需要实现 com.sun.tools.doclets.Taglet 接口,要实现它所有的方法。里面有一片的 inField(),inMethod() 等方法,若返回 true 则表示这个标签可作用于这个位置上。
那什么是 isInlineTag() 呢,下面这样写的注释就是 Inline Tag: 用大括号括起来的就是 Inline tag,这时取的 tag.text() 就只是 "help" 了。你自己决定 isInlineTag() 方法返回 true 还是 false 吧。
1 2 3 4 5 6 |
/** * Inline: {@document help} yourself */ public static void main(String[] args) { } |
还有那个注册方法,需 Taglet 接口中没有,但却是一定要写的,方法原型是:
public static void register(Map<String, Taglet> tagletMap),用来注册要用到的所有 Taglet, 也就是可在此一个个指定什么 tag 由哪一个类来处理。其实这里的 DocumentTaglet 可不实现 Taglet, javadoc -taglet 指定为这个类后所要做的工作就是执行该类的 register(Map<String, Taglet> tagletMap) 方法,至于后面碰到了某个 tag 时才真正去调用对应 Taglet 类的 toString 方法。
也就是说在这个 register 方法中可以同时注册多个 Taglet 类实例。这种情况下把现在的 DocumentTaglet 更名为 CustomTags 意义就更准确些。
关键是那两个 toString() 方法,它的返回值就是要交给 javadoc 显示出来的内容,文章要在这儿做。
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
package cc.unmi.taglet; import java.util.Map; import com.sun.javadoc.Tag; import com.sun.tools.doclets.Taglet; public class DocumentTaglet implements Taglet { private static final String NAME = "document"; private static final String HEADER = "Document:"; @Override public String getName() { return NAME; } @Override public String toString(Tag[] tags) { StringBuilder result = new StringBuilder(); for (Tag tag : tags) { result.append("\n<dt><b>" + HEADER + "</b>"); result.append("<dd style='color:red'>" + tag.text() + "</dd>"); } //or do anything here ...... return result.toString(); } @Override public String toString(Tag tag) { return toString(new Tag[] { tag }); } public static void register(Map<String, Taglet> tagletMap) { DocumentTaglet tag = new DocumentTaglet(); Taglet t = (Taglet) tagletMap.get(tag.getName()); if (t != null) { tagletMap.remove(tag.getName()); } tagletMap.put(tag.getName(), tag); } @Override public boolean inConstructor() { return false; } @Override public boolean inField() { return false; } @Override public boolean inMethod() { return true; } @Override public boolean inOverview() { return false; } @Override public boolean inPackage() { return false; } @Override public boolean inType() { return false; } @Override public boolean isInlineTag() { return true; } } |
把重要的代码前移了,因为用到了 tools.jar 包,所以编译时需把 JDK 目录中的 tools.jar 加到 classpath 上去。实际测试中不管为一个方法写一个还是多个 @document 注释,javadoc 都是直接命中 toString(Tag[] tags) 方法。
要是想在 toString(Tag[] tags) 方法中得到被注释的元素的信息,就从 Tag 类型出发,tag.holder() 得到一个 Doc,对应注释的位置,它可能是一个 MethodDoc, ClassDoc, FieldDoc, PackageDoc 等,所以当前被注释元素的信息便可由此而得。
在假设编译后的 DocumentTaglet.class 文件在 c:/javadoc_taglet/bin/cc/unmi/taglet 目录下,你可以用下面的命令为前面那个 TestJavaDocTag.java 生成 javadoc 文档:
javadoc -taglet cc.unmi.taglet.DocumentTaglet -tagletpath c:/javadoc_taglet/bin
生成的 javadoc 的效果就是, @document 的值提取出来,并渲染为红色字体显示:
我们在生成 javadoc 时,会得到提示:
Note: Custom tags that could override future standard tags: @document. To avoid
potential overrides, use at least one period character (.) in custom tag names.
那是 javadoc 怕 @document 会与别人重复,希望你能加个命名空间,如写成 @unmi.document 这样子。
tools.jar 中还有另外一个 Taglet(com.sun.tools.doclets.internal.toolkit.taglets.Taglet) 接口,尚不知作用何的,Doclet 吗?接着有空也看看 -doclet 参数的应用定制。
参考:1. javadoc - The Java API Documentation Generator
2. Taglet Overview
3. Javadoc Programming
4. Taglets Collection
本文链接 https://yanbin.blog/javadoc-programming-customize-taglet/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。