Java Doclet
[TOC]
1. 简介
Doclet是用Java编程语言编写的程序,它用doclet API指定Javadoc工具的输出内容和格式。缺省情况下,Javadoc工具用Sun提供的"标准"doclet来生成HTML形式的API文档。然而,用户也可用自己的doclet根据个人喜好自定义Javadoc输出。用户可以利用doclet API从头开始编写doclet,也可以对标准doclet进行修改,以适合自己的需要。
2. 创建自己的Doclet
-
引入doclet API
引入import com.sun.javadoc.*
,程序的入口方法是public static boolean start(RootDoc root)
例子:
import com.sun.javadoc.*; public class ListClass { public static boolean start(RootDoc root) { ClassDoc[] classes = root.classes(); for (int i = 0; i < classes.length; ++i) { System.out.println(classes[i]); } return true; } }
-
编译代码
javac ListClass.java
-
javadoc运行
javadoc -doclet ListClass -docletpath . MyClass.java
3. 自定义标签的处理
注释中包含如下标签
@mytag Some dummy text
通过Doc或者其子类的tags(String)
方法获取,返回:"Some dummy text."
method.tags("mytag")
例子:
import com.sun.javadoc.*;
public class ListTags {
public static boolean start(RootDoc root){
String tagName = "mytag";
writeContents(root.classes(), tagName);
return true;
}
private static void writeContents(ClassDoc[] classes, String tagName) {
for (int i = 0; i < classes.length; i++) {
boolean classNamePrinted = false;
MethodDoc[] methods = classes[i].methods();
for (int j = 0; j < methods.length; j++) {
Tag[] tags = methods[j].tags(tagName);
if (tags.length > 0) {
if (!classNamePrinted) {
System.out.println("\n" + classes[i].name() + "\n");
classNamePrinted = true;
}
System.out.println(methods[j].name());
for (int k = 0; k < tags.length; k++) {
System.out.println(" " + tags[k].name() + ": " + tags[k].text());
}
}
}
}
}
}
4. 自定义命令行选项
新增自定义选项的名字可以自定义,但是选项默认是无参数,如果需要参数,则需要做额外工作。
选项的结构
Rootdoc.options()方法返回一个二维String数组 String[][],如下:
options()[0][0] = "-foo"
options()[0][1] = "this"
options()[0][2] = "that"
options()[1][0] = "-bar"
options()[1][1] = "other"
自定义选项参数个数
每个选项的参数个数是不定,需要自定义Doclet拥有一个public static int optionLength(String option)
方法:
public static int optionLength(String option) {
if(option.equals("-tag")) {
return 2;
}
return 0;
}
返回值是参数的个数,如果是不需识别的选项,返回0即可。
自定义选项参数校验
参数校验需要自定义Doclet拥有一个public static boolean validOptions(String options[][], DocErrorReporter reporter)
方法:
public static boolean validOptions(String options[][],
DocErrorReporter reporter) {
boolean foundTagOption = false;
for (int i = 0; i < options.length; i++) {
String[] opt = options[i];
if (opt[0].equals("-tag")) {
if (foundTagOption) {
reporter.printError("Only one -tag option allowed.");
return false;
} else {
foundTagOption = true;
}
}
}
if (!foundTagOption) {
reporter.printError("Usage: javadoc -tag mytag -doclet ListTags ...");
}
return foundTagOption;
}
方法内实现参数校验逻辑,并通过DocErrorReporter.printError
打印错误信息。
抽象的Doclet类
JDK中提供了一个抽象的Doclet类,可直接继承使用。因为RootDoc的实现类RootDocImpl并没面向接口实现,而是利用反射实现了鸭子类型,可参考类com.sun.tools.javadoc.main.DocletInvoker
的实现。所以继承com.sun.javadoc.Doclet抽象类并不是必须的。
package com.sun.javadoc;
@Deprecated(since="9", forRemoval=true)
@SuppressWarnings("removal")
public abstract class Doclet {
public static boolean start(RootDoc root) {
return true;
}
public static int optionLength(String option) {
return 0; // default is option unknown
}
public static boolean validOptions(String options[][],
DocErrorReporter reporter) {
return true; // default is options are valid
}
public static LanguageVersion languageVersion() {
return LanguageVersion.JAVA_1_1;
}
}
5. javadoc
工具的外部入口
外部入口类com.sun.tools.javadoc.Main:
package com.sun.tools.javadoc;
public class Main {
private Main() {
}
/**
* Command line interface.
* @param args The command line parameters.
*/
public static void main(String... args) {
System.exit(execute(args));
}
/**
* Programmatic interface.
* @param args The command line parameters.
* @return The return code.
*/
public static int execute(String... args) {
Start jdoc = new Start();
return jdoc.begin(args);
}
......
}
如果需要在代码中出发执行javadoc,可以这么做:
import com.sun.tools.javadoc.Main;
String[] commandLineParams = new String[]{
"-encoding", "utf-8",
"-sourcepath", "/path/to/src/main/java"
};
Main.execute(commandLineParams);
commandLineParams为命令行的参数数组。
6. 获取泛型类型
泛型是JDK5之后才有的,所以要指定语言版本:
public static LanguageVersion languageVersion() {
return LanguageVersion.JAVA_1_5;
}
获取方式:
if (type instanceof ParameterizedType) {
ParameterizedType pt = type.asParameterizedType();
Type[] typeArgs = pt.typeArguments();
}
7. 获取私有类、私有属性、私有方法
获取私有属性和私有方法可以通过传入参数实现
public static boolean start(RootDoc root) {
ClassDoc[] classes = root.classes();
for (ClassDoc cls : classes) {
System.out.println(cls);
FieldDoc[] fields = cls.fields(false); // 获取包含私有属性在内的所有属性
for (FieldDoc flc : fields) {
System.out.println(flc);
}
MethodDoc[] methods = cls.methods(false); // 获取包含私有方法在内的所有方法
for (MethodDoc meth : methods) {
System.out.println(meth);
}
}
return true;
}
获取私有类只能通过传入-private
选项给javadoc实现。
8. Maven中使用自定义Doclet
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<encoding>UTF-8</encoding>
<useStandardDocletOptions>false</useStandardDocletOptions>
<doclet>com.my.CustomMavenDoclet</doclet>
<docletArtifacts>
<docletArtifact>
<groupId>com.my.component</groupId>
<artifactId>doclet</artifactId>
<version>1.0-SNAPSHOT</version>
</docletArtifact>
</docletArtifacts>
</configuration>
</plugin>
9. Java9之后的javadoc
java9的javadoc做了较大改变,代码放到了模块jdk.javadoc下,比如外部入口类是jdk.javadoc.internal.tool.Main
。
10. javadoc选项及参数说明
-
开头是选项的名字
@
是从指定文件加载选项即参数
- sourcepath
:指定源码地址,多个路径以:
分割
javadoc的参数可以是类名,也可以是包名,但是如果是包,javadoc只生成指定包下的类,不支持递归。
网友评论