java 文件自動生成的神器 idoc

老馬嘯西風 發表於 2021-10-19
Java

寫文件

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

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

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

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

在這裡插入圖片描述

寫文件最令我不爽的地方是在於程式碼備註要改一遍,然後文件再改一遍。

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

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

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~

在這裡插入圖片描述