美文网首页
Java Doclet

Java Doclet

作者: 默兮扬兮 | 来源:发表于2019-11-24 18:53 被阅读0次

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

    相关文章

      网友评论

          本文标题:Java Doclet

          本文链接:https://www.haomeiwen.com/subject/optrwctx.html