java 文檔自動生成的神器 idoc


寫文檔

作為一名開發者,每個人都要寫代碼。

工作中,幾乎每一位開發者都要寫文檔。

因為工作是人和人的協作,產品要寫需求文檔,開發要寫詳細設計文檔,接口文檔。

可是,作為一個懶人,平時最討厭的一件事情就是寫文檔。

在這里插入圖片描述

寫文檔最令我不爽的地方是在於代碼備注要改一遍,然后文檔再改一遍。

所有重復的勞作,都是對於我們寶貴摸魚時間的最大浪費。

於是,我就常常想,能不能只寫一遍呢?

i-doc 項目簡介

idoc 為 java 項目生成項目文檔。

基於原生的 java 注釋,盡可能的生成簡介的文檔。用戶可以自定義自己的模板,生成自己需要的文檔。

實現原理:基於 maven 插件,類似於 javadoc。可以更加靈活,允許用戶自定義。

特性

(1)基於 maven 項目生成包含大部分信息的元數據

(2)默認支持 markdown 簡化文檔的生成,支持自定義模板

(3)支持用戶自定義文檔生成器

(4)支持用戶自定生成文檔的類過濾器

(5)添加字段類型別名,支持用戶自定義

快速入門

需要

jdk1.8+

maven 3.x+

maven 引入

使用 maven 引入當前 idoc 插件。

<build>
    <plugins>
        <plugin>
            <groupId>com.github.houbb</groupId>
            <artifactId>idoc-core</artifactId>
            <version>0.3.0</version>
        </plugin>
    </plugins>
</build>

測試對象的創建

為了演示文檔,我們創建了一個 Address 對象。

package com.github.houbb.idoc.test.model;

/**
 * 地址
 * @author binbin.hou
 * @since 0.0.1
 */
public class Address {

    /**
     * 城市
     */
    private String country;

    /**
     * 街道
     */
    private String street;

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }
}

執行插件

mvn com.github.houbb:idoc-core:0.3.0:idoc

命令行日志信息

[INFO] ------------------------------------ Start generate doc
[INFO] 共計 【1】 個文件待處理,請耐心等待。進度如下:
==================================================================================================== 100%
[INFO] Generator doc with docGenerator: com.github.houbb.idoc.core.api.generator.ConsoleDocGenerator
[INFO] ------------------------------------ 文檔信息如下:

[類名] com.github.houbb.idoc.test.model.Address
[類信息] {"comment":"地址","docAnnotationList":[],"docFieldList":[{"comment":"城市","name":"country","type":"java.lang.String"},{"comment":"街道","name":"street","type":"java.lang.String"}],"docMethodList":[{"docMethodParameterList":[],"docMethodReturn":{"fullName":"java.lang.String","name":"String","packageName":"java.lang"},"docTagList":[],"exceptionList":[],"modifiers":["public"],"name":"getCountry","seeList":[],"signature":"getCountry()"},{"docMethodParameterList":[{"docAnnotationList":[],"name":"country","type":"java.lang.String"}],"docMethodReturn":{},"docTagList":[],"exceptionList":[],"modifiers":["public"],"name":"setCountry","seeList":[],"signature":"setCountry(country)"},{"docMethodParameterList":[],"docMethodReturn":{"fullName":"java.lang.String","name":"String","packageName":"java.lang"},"docTagList":[],"exceptionList":[],"modifiers":["public"],"name":"getStreet","seeList":[],"signature":"getStreet()"},{"docMethodParameterList":[{"docAnnotationList":[],"name":"street","type":"java.lang.String"}],"docMethodReturn":{},"docTagList":[],"exceptionList":[],"modifiers":["public"],"name":"setStreet","seeList":[],"signature":"setStreet(street)"}],"docTagList":[{"lineNum":5,"name":"author","parameters":["binbin.hou"],"value":"binbin.hou"},{"lineNum":6,"name":"since","parameters":["0.0.1"],"value":"0.0.1"}],"fullName":"com.github.houbb.idoc.test.model.Address","modifiers":["public"],"name":"Address","packageName":"com.github.houbb.idoc.test.model"}

[INFO] ------------------------------------ Finish generate doc

更多生成方式

當然,你可以發現這里只是把元數據進行輸出到控台,意義不大。

我們可以根據需求,自定義實現生成類。

比如下面的方式,可以使用內置的 MarkdownDocGenerator 輸出到 markdown 文件。

<plugin>
    <groupId>com.github.houbb</groupId>
    <artifactId>idoc-core</artifactId>
    <version>0.3.0</version>
    <configuration>
        <generates>
            <generate>com.github.houbb.idoc.ftl.api.generator.MarkdownDocGenerator</generate>
        </generates>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>com.github.houbb</groupId>
            <artifactId>idoc-ftl</artifactId>
            <version>0.3.0</version>
        </dependency>
    </dependencies>
</plugin>

效果可以參考:

heaven 文檔目錄

ps: heaven 項目是個人整理了多年的工具包,幾百個類,手寫文檔估計要很久。

設計初衷

節約時間

Java 文檔一直是一個大問題。

很多項目不寫文檔,即使寫文檔,對於開發人員來說也是非常痛苦的。

不寫文檔的缺點自不用多少,手動寫文檔的缺點也顯而易見:

  1. 非常浪費時間,而且會出錯。

  2. 無法保證及時更新。代碼已經變了,但是文檔還要同步修改。需要強制人來維護這一種一致性。這很難。

為什么不是 swagger-ui

java 的文檔有幾類:

  1. jdk 自帶的 doc 生成。這個以前實踐給別人用過,別人用 C#,看到 java 的默認文檔感覺很痛苦。

就算是我們 java 開發者,也很討厭看 jdk 的文檔。看着不美觀,也很累。

  1. swagger-ui 是基於 java 注解的文檔生成工具。相對而言比較優雅,也非常強大。

但是缺點也是有的。開發人員要寫 jdk 原來的注釋+注解。注解太多,導致寫起來也很痛苦,大部分開發者后來還是選擇了放棄。

那么問題來了?我們怎么辦才能盡可能的讓開發人員,和文檔閱讀人員都樂於接受呢?

jdk 自帶的 doc 就是基於 maven 插件的,本項目也是。

區別如下:

  1. 盡可能的保證和 Java 原生注釋一致,讓開發者很容易就可以使用。

  2. 盡可能的信息全面,但是文檔簡潔。讓文檔的閱讀者享受到等同於手寫文檔的體驗。

  3. 將信息的獲取和生成區分開。方便用戶自己定義自己的輸出方式。

參數配置說明

為了更加靈活的實現文檔的生成和文檔元數據的生成,提供如下參數

插件配置屬性簡介

屬性 是否必填 說明 默認值 備注
encoding 項目編碼 UTF-8
includes 元數據包含的文件信息 **\/*.java 默認掃描所有 java 文件
excludes 元數據排除的文件信息 默認不排除
isOverwriteWhenExists 文檔存在時是否覆蓋 true
isAllInOne 所有類信息是否生成單個文檔 true 命令行文檔生成器,此屬性無意義。
generates 文檔生成類 命令行文檔生成信息 可以同時指定多個。類名全稱。用戶自定義參見 com.github.houbb.idoc.api.core.genenrator.IDocGenerator
generateFilters 文檔生成類過濾器 可以同時指定多個。類名全稱。用戶自定義參見 com.github.houbb.idoc.api.core.filter.IDocGenerateFilter
targetDir 生成目標文件目錄 自定義指定文檔生成的文件夾

isAllInOne

簡單的文檔,建議直接生成在一個文件。

如果較為復雜,則可以設為 false,則會按照

generates 相關問題

默認的命令行文檔,主要用於演示和信息查看,不具有實際意義。

建議引入 idoc-ftl 模塊,使用 MarkdownDocGenerator 生成器。

可以同時指定多個。

可引入 idoc-api 自行定義。

generateFilters 建議

實際的文檔,主要關心定義的方法。

我們可以針對 DocClass 的包名,過濾只生成 Service 方法文檔。

如果是在以前的基礎上,則可以加上 @since @version 等信息的過濾。

可以同時指定多個。

可引入 idoc-api 自行定義。

自定義 Filter

可以參考當前項目的 idoc-test 模塊。

整體配置如下:

<build>
    <plugins>
        <plugin>
            <groupId>com.github.houbb</groupId>
            <artifactId>idoc-core</artifactId>
            <version>0.3.0</version>
            <configuration>
                <isAllInOne>true</isAllInOne>
                <generates>
                    <generate>com.github.houbb.idoc.ftl.api.generator.MarkdownDocGenerator</generate>
                </generates>
                <generateFilters>
                    <generateFilter>com.github.houbb.idoc.test.filter.MyGenerateFilter</generateFilter>
                </generateFilters>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>com.github.houbb</groupId>
                    <artifactId>idoc-test</artifactId>
                    <version>${project.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

指定文檔生成器

指定使用 Markdown 文檔生成器,可以同時指定多個。

<generates>
    <generate>com.github.houbb.idoc.ftl.api.generator.MarkdownDocGenerator</generate>
</generates>

引入包

MarkdownDocGenerator 在 idoc-ftl 模塊中,需要引入對應的依賴。

當然 idoc-core 默認依賴 idoc-ftl

指定文件生成類的過濾器

如果不定義自己的類生成過濾器,則會生成所有的類信息。

一般使用中我們只關心 service 方法,所以添加了類的過濾實現。

實現如下:

引入 idoc-api 包

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>idoc-api</artifactId>
    <version>${project.version}</version>
</dependency>

實現 IDocGenerateFilter

package com.github.houbb.idoc.test.filter;

import com.github.houbb.idoc.api.core.filter.IDocGenerateFilter;
import com.github.houbb.idoc.api.model.metadata.DocClass;

/**
 * 自定義生成過濾器
 * @author binbin.hou
 * @since 0.0.1
 */
public class MyGenerateFilter implements IDocGenerateFilter {

    @Override
    public boolean include(DocClass docClass) {
        if("QueryUserService".equalsIgnoreCase(docClass.getName())) {
            return true;
        }
        return false;
    }

}

插件中配置使用

<generateFilters>
    <generateFilter>com.github.houbb.idoc.test.filter.MyGenerateFilter</generateFilter>
</generateFilters>

注意,也需要將你定義這個過濾器的 jar 添加依賴,否則無法找到對應的類信息。

<dependencies>
    <dependency>
        <groupId>com.github.houbb</groupId>
        <artifactId>idoc-test</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

類代碼信息

User 信息

/**
 * 用戶信息
 * @author binbin.hou
 * @since 0.0.1
 */
public class User {

    /**
     * 名稱
     * @require 是
     * @remark 中文名稱,請認真填寫
     */
    private String name;

    /**
     * 年齡
     */
    private int age;

    /**
     * 生日
     */
    private Date birthday;

    /**
     * 地址
     */
    private List<Address> addressList;

    /**
     * 伴侶
     */
    private User mate;
    
    //...
}

i-doc 定義的標簽

@require 表示當前字段是否必填,作為方法入參時。

@remark 表示當前字段的備注信息。

方法類信息

  • QueryUserService.java
/**
 * 查詢用戶服務類
 * @author binbin.hou
 * @since 0.0.1
 */
public interface QueryUserService {

    /**
     * 根據用戶信息查詢用戶
     * @param user 用戶信息
     * @return 結果
     * @since 0.0.2,2019/02/12
     */
    public User queryUser(final User user);

}

執行插件

mvn com.github.houbb:idoc-core:0.3.0:idoc
  • 日志信息
[INFO] ------------------------------------ Start generate doc
[INFO] 共計 【4】 個文件待處理,請耐心等待。進度如下:
==================================================================================================== 100%
[INFO] Generator doc with docGenerator: com.github.houbb.idoc.ftl.api.generator.MarkdownDocGenerator
[INFO] Markdown 生成文檔文件 all in one 路徑: /Users/houbinbin/code/_github/idoc/idoc-test/src/main/resources/idoc-gen/idoc-test-全部文檔.md
[INFO] ------------------------------------ Finish generate doc

文檔信息

當前文件路徑日志會打印,比如我自己測試的為:

/Users/houbinbin/code/_github/idoc/idoc-test/src/main/resources/idoc-gen/idoc-test-全部文檔.md

文檔生成效果

參見文檔:

idoc-test-全部文檔.md

字段類型別名支持

可以參考當前項目的 idoc-test 模塊。

為什么需要

有時候頁面顯示類型,希望更加友好。

所以系統內置了一些別名顯示,也同時支持自定義別名。

類型字段的別名

系統內置

系統當前版本提供了常見的別名。

詳情見 com.github.houbb.idoc.core.util.JavaTypeAliasUtil

類型 別稱
java.lang.Float 浮點型
java.lang.Double 浮點型
java.util.Date 日期
java.time.LocalDateTime 日期時間
java.util.Currency 貨幣
float 浮點型
java.lang.Integer 整型
long 長整型
java.math.BigDecimal 數字
java.lang.Character 字符
java.lang.Long 長整型
java.lang.Short 短整型
java.util.Map 映射
java.time.LocalTime 時間
java.lang.Boolean 布爾值
java.math.BigInteger 數字
java.lang.String 字符串
java.lang.Byte 字節
double 浮點型
byte 字節
java.util.Collection 集合
int 整型
java.util.List 列表
boolean 布爾值
java.time.LocalDate 日期
char 字符
short 短整型
void
array 數組

自定義的方式

可以通過 typeAlias 指定自定義的字段別稱。

<configuration>
    <generateFilters>
        <generateFilter>com.github.houbb.idoc.test.filter.MyGenerateFilter</generateFilter>
    </generateFilters>
    <isAllInOne>true</isAllInOne>
    <typeAliases>
        <typeAlias>
            <key>java.lang.String</key>
            <value>String自定義說明</value>
        </typeAlias>
    </typeAliases>
</configuration>

優先級

用戶自定義的字段別名優先級高於系統默認。

后面定義的別名會直接覆蓋前面的別名。

測試代碼演示

對象定義

/**
 * 別名測試
 * @author binbin.hou
 * @since 0.0.1
 */
public class TypeAliasSimpleBean {

    /**
     * 名稱
     */
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

測試日志

運行測試日志如下:

{"comment":"別名測試","docAnnotationList":[],"docFieldList":[{"comment":"名稱","name":"name","type":"java.lang.String","typeAlias":"String自定義說明"}],"docMethodList":[{"docMethodParameterList":[],"docMethodReturn":{"fullName":"java.lang.String","name":"String","packageName":"java.lang"},"docTagList":[],"exceptionList":[],"modifiers":["public"],"name":"getName","seeList":[],"signature":"getName()"},{"docMethodParameterList":[{"docAnnotationList":[],"name":"name","type":"java.lang.String","typeAlias":"String自定義說明"}],"docMethodReturn":{},"docTagList":[],"exceptionList":[],"modifiers":["public"],"name":"setName","seeList":[],"signature":"setName(name)"}],"docTagList":[{"lineNum":5,"name":"author","parameters":["binbin.hou"],"value":"binbin.hou"},{"lineNum":6,"name":"since","parameters":["0.0.1"],"value":"0.0.1"}],"fullName":"com.github.houbb.idoc.test.model.TypeAliasSimpleBean","modifiers":["public"],"name":"TypeAliasSimpleBean","packageName":"com.github.houbb.idoc.test.model"}

其中 typeAlias 就是字段類型的別名,我們可以用來更加友好的顯示字段信息。

其他的思考

自定義方式的便利性

自定義的方式采用基於 xml 的方式是比較方便。

但是數量比較多的時候就沒有那么方便,本來考慮添加對應的配置屬性接口,權衡下還是使用了 xml 配置的方式。

是否使用 comment 信息?

如果一個字段,沒有指定別名,是否使用 comment 信息做替代?

建議使用,當前版本不做處理。

  • 為什么使用

比起冗長的類信息,大部分人更樂於看到解釋。

如果是針對同構的系統(都是 java 語言),則可以理解。

如果是針對異構的系統(比如前台是 php),則不易於理解。

  • 為什么不處理

大部分的接口都是常見字段, 性價比不高。

可能存在字段沒有些 comment 的情況,會導致判斷的復雜性。

如果用戶不想使用別名

直接修改模板即可,使用原來的字段 type 屬性即可。

開源地址

https://github.com/houbb/idoc

當然,這個項目還有很長的路要走。

如果喜歡,歡迎 fork star~

在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM