寫文件
作為一名開發者,每個人都要寫程式碼。
工作中,幾乎每一位開發者都要寫文件。
因為工作是人和人的協作,產品要寫需求文件,開發要寫詳細設計文件,介面文件。
可是,作為一個懶人,平時最討厭的一件事情就是寫文件。
寫文件最令我不爽的地方是在於程式碼備註要改一遍,然後文件再改一遍。
所有重複的勞作,都是對於我們寶貴摸魚時間的最大浪費。
於是,我就常常想,能不能只寫一遍呢?
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 文件一直是一個大問題。
很多專案不寫文件,即使寫文件,對於開發人員來說也是非常痛苦的。
不寫文件的缺點自不用多少,手動寫文件的缺點也顯而易見:
- 非常浪費時間,而且會出錯。
- 無法保證及時更新。程式碼已經變了,但是文件還要同步修改。需要強制人來維護這一種一致性。這很難。
為什麼不是 swagger-ui
java 的文件有幾類:
- jdk 自帶的 doc 生成。這個以前實踐給別人用過,別人用 C#,看到 java 的預設文件感覺很痛苦。
就算是我們 java 開發者,也很討厭看 jdk 的文件。看著不美觀,也很累。
- swagger-ui 是基於 java 註解的文件生成工具。相對而言比較優雅,也非常強大。
但是缺點也是有的。開發人員要寫 jdk 原來的註釋+註解。註解太多,導致寫起來也很痛苦,大部分開發者後來還是選擇了放棄。
那麼問題來了?我們怎麼辦才能儘可能的讓開發人員,和文件閱讀人員都樂於接受呢?
jdk 自帶的 doc 就是基於 maven 外掛的,本專案也是。
區別如下:
- 儘可能的保證和 Java 原生註釋一致,讓開發者很容易就可以使用。
- 儘可能的資訊全面,但是文件簡潔。讓文件的閱讀者享受到等同於手寫文件的體驗。
- 將資訊的獲取和生成區分開。方便使用者自己定義自己的輸出方式。
引數配置說明
為了更加靈活的實現文件的生成和文件後設資料的生成,提供如下引數
外掛配置屬性簡介
屬性 | 是否必填 | 說明 | 預設值 | 備註 |
---|---|---|---|---|
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~