Java Doclet

Java Doclet

[TOC]

1. 简介

Doclet是用Java编程语言编写的程序,它用doclet API指定Javadoc工具的输出内容和格式。缺省情况下,Javadoc工具用Sun提供的"标准"doclet来生成HTML形式的API文档。然而,用户也可用自己的doclet根据个人喜好自定义Javadoc输出。用户可以利用doclet API从头开始编写doclet,也可以对标准doclet进行修改,以适合自己的需要。

2. 创建自己的Doclet

  1. 引入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;
        }
    }
    
  2. 编译代码

    javac ListClass.java 
    
  3. 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只生成指定包下的类,不支持递归。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • 对象的创建与销毁 Item 1: 使用static工厂方法,而不是构造函数创建对象:仅仅是创建对象的方法,并非Fa...
    孙小磊阅读 1,876评论 0 3
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,614评论 0 3
  • 小编费力收集:给你想要的面试集合 1.C++或Java中的异常处理机制的简单原理和应用。 当JAVA程序违反了JA...
    八爷君阅读 4,484评论 1 114
  • 1. Annotations(注解:5个) Annotation Use Style(注解使用风格) 这项检查可以...
    onlyHalfSoul阅读 5,715评论 1 1
  • 面向对象主要针对面向过程。 面向过程的基本单元是函数。 什么是对象:EVERYTHING IS OBJECT(万物...
    sinpi阅读 1,020评论 0 4