通過Swagger系列可以快速生成API文檔,但是這種API文檔生成是需要在接口上添加註解等,這表明這是一種侵入式方式; 那麼有沒有非侵入式方式呢, 比如通過註釋生成文檔? 本文主要介紹非侵入式的方式及集成Smart-doc案例。我們構建知識體系時使用Smart-doc這類工具並不是目標,而是要了解非侵入方式能做到什麼程度和技術思路。 @pdai
準備知識點
需要了解Swagger侵入性和依賴性, 以及Smart-Doc這類工具如何解決這些問題, 部分內容來自官方網站。@pdai
爲什麼會產生Smart-Doc這類工具?
既然有了Swagger, 爲何還會產生Smart-Doc這類工具呢? 本質上是Swagger侵入性和依賴性。
我們來看下目前主流的技術文檔工具存在什麼問題:
- 侵入性強,需要編寫大量註解,代表工具如:swagger,還有一些公司自研的文檔工具
- 強依賴性,如果項目不想使用該工具,業務代碼無法編譯通過。
- 代碼解析能力弱,使用文檔不齊全,主要代表爲國內衆多開源的相關工具。
- 衆多基於註釋分析的工具無法解析jar包裏面的註釋(sources jar包),需要人工配置源碼路徑,無法滿足DevOps構建場景。
- 部分工具無法支持多模塊複雜項目代碼分析。
什麼是Smart-Doc?有哪些特性?
smart-doc是一款同時支持JAVA REST API和Apache Dubbo RPC接口文檔生成的工具,smart-doc在業內率先提出基於JAVA泛型定義推導的理念, 完全基於接口源碼來分析生成接口文檔,不採用任何註解侵入到業務代碼中。你只需要按照java-doc標準編寫註釋, smart-doc就能幫你生成一個簡易明瞭的Markdown、HTML5、Postman Collection2.0+、OpenAPI 3.0+的文檔。
- 零註解、零學習成本、只需要寫標準JAVA註釋。
- 基於源代碼接口定義自動推導,強大的返回結構推導。
- 支持Spring MVC、Spring Boot、Spring Boot Web Flux(controller書寫方式)、Feign。
- 支持Callable、Future、CompletableFuture等異步接口返回的推導。
- 支持JavaBean上的JSR303參數校驗規範,包括分組驗證。
- 對JSON請求參數的接口能夠自動生成模擬JSON參數。
- 對一些常用字段定義能夠生成有效的模擬值。
- 支持生成JSON返回值示例。
- 支持從項目外部加載源代碼來生成字段註釋(包括標準規範發佈的jar包)。
- 支持生成多種格式文檔:Markdown、HTML5、Asciidoctor、Postman Collection、OpenAPI 3.0。 Up- 開放文檔數據,可自由實現接入文檔管理系統。
- 支持導出錯誤碼和定義在代碼中的各種字典碼到接口文檔。
- 支持Maven、Gradle插件式輕鬆集成。
- 支持Apache Dubbo RPC接口文檔生成。
- debug接口調試html5頁面完全支持文件上傳,下載(@download tag標記下載方法)測試。
實現案例
從smart-doc 1.7.9開始官方提供了Maven插件,可以通過在項目中集成smart-doc的Maven插件,然後運行插件直接生成文檔。 我們的案例基於smart-doc-maven-plugin,生成文檔。示例參考官方配置文檔而寫。
配置
添加maven的插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.github.shalousun</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<version>2.4.8</version>
<configuration>
<!--指定生成文檔的使用的配置文件,配置文件放在自己的項目中-->
<configFile>./src/main/resources/smart-doc.json</configFile>
<!--指定項目名稱,推薦使用動態參數,例如${project.description}-->
<!--如果smart-doc.json中和此處都未設置projectName,2.3.4開始,插件自動採用pom中的projectName作爲設置-->
<!--<projectName>${project.description}</projectName>-->
<!--smart-doc實現自動分析依賴樹加載第三方依賴的源碼,如果一些框架依賴庫加載不到導致報錯,這時請使用excludes排除掉-->
<excludes>
<!--格式爲:groupId:artifactId;參考如下-->
<!--也可以支持正則式如:com.alibaba:.* -->
<exclude>com.alibaba:fastjson</exclude>
</excludes>
<!--includes配置用於配置加載外部依賴源碼,配置後插件會按照配置項加載外部源代碼而不是自動加載所有,因此使用時需要注意-->
<!--smart-doc能自動分析依賴樹加載所有依賴源碼,原則上會影響文檔構建效率,因此你可以使用includes來讓插件加載你配置的組件-->
<includes>
<!--格式爲:groupId:artifactId;參考如下-->
<!--也可以支持正則式如:com.alibaba:.* -->
<include>com.alibaba:fastjson</include>
</includes>
</configuration>
<executions>
<execution>
<!--如果不需要在執行編譯時啓動smart-doc,則將phase註釋掉-->
<phase>compile</phase>
<goals>
<!--smart-doc提供了html、openapi、markdown等goal,可按需配置-->
<goal>html</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
其中./src/main/resources/smart-doc.json是配置文件。
{
"serverUrl": "http://127.0.0.1", //服務器地址,非必須。導出postman建議設置成http://{{server}}方便直接在postman直接設置環境變量
"pathPrefix": "", //設置path前綴,非必須。如配置Servlet ContextPath 。@since 2.2.3
"isStrict": false, //是否開啓嚴格模式
"allInOne": true, //是否將文檔合併到一個文件中,一般推薦爲true
"outPath": "D://md2", //指定文檔的輸出路徑
"coverOld": true, //是否覆蓋舊的文件,主要用於markdown文件覆蓋
"createDebugPage": true,//@since 2.0.0 smart-doc支持創建可以測試的html頁面,僅在AllInOne模式中起作用。
"packageFilters": "",//controller包過濾,多個包用英文逗號隔開,2.2.2開始需要採用正則:com.test.controller.*
"md5EncryptedHtmlName": false,//只有每個controller生成一個html文件時才使用
"style":"xt256", //基於highlight.js的代碼高設置,可選值很多可查看碼雲wiki,喜歡配色統一簡潔的同學可以不設置
"projectName": "pdai-springboot-demo-smart-doc",//配置自己的項目名稱,不設置則插件自動獲取pom中的projectName
"skipTransientField": true,//目前未實現
"sortByTitle":false,//接口標題排序,默認爲false,@since 1.8.7版本開始
"showAuthor":true,//是否顯示接口作者名稱,默認是true,不想顯示可關閉
"requestFieldToUnderline":true,//自動將駝峯入參字段在文檔中轉爲下劃線格式,//@since 1.8.7版本開始
"responseFieldToUnderline":true,//自動將駝峯入參字段在文檔中轉爲下劃線格式,//@since 1.8.7版本開始
"inlineEnum":true,//設置爲true會將枚舉詳情展示到參數表中,默認關閉,//@since 1.8.8版本開始
"recursionLimit":7,//設置允許遞歸執行的次數用於避免一些對象解析卡主,默認是7,正常爲3次以內,//@since 1.8.8版本開始
"allInOneDocFileName":"index.html",//自定義設置輸出文檔名稱, @since 1.9.0
"requestExample":"true",//是否將請求示例展示在文檔中,默認true,@since 1.9.0
"responseExample":"true",//是否將響應示例展示在文檔中,默認爲true,@since 1.9.0
"ignoreRequestParams":[ //忽略請求參數對象,把不想生成文檔的參數對象屏蔽掉,@since 1.9.2
"org.springframework.ui.ModelMap"
],
"dataDictionaries": [{ //配置數據字典,沒有需求可以不設置
"title": "http狀態碼字典", //數據字典的名稱
"enumClassName": "tech.pdai.springboot.smartdoc.constant.ResponseStatus", //數據字典枚舉類名稱
"codeField": "responseCode",//數據字典字典碼對應的字段名稱
"descField": "description"//數據字典對象的描述信息字典
}],
"errorCodeDictionaries": [{ //錯誤碼列表,沒有需求可以不設置
"title": "title",
"enumClassName": "tech.pdai.springboot.smartdoc.constant.ResponseStatus", //錯誤碼枚舉類
"codeField": "responseCode",//錯誤碼的code碼字段名稱
"descField": "description"//錯誤碼的描述信息對應的字段名
}],
"revisionLogs": [{ //文檔變更記錄,非必須
"version": "1.1", //文檔版本號
"revisionTime": "2022-07-01 22:12:01", //文檔修訂時間
"status": "update", //變更操作狀態,一般爲:創建、更新等
"author": "pdai", //文檔變更作者
"remarks": "init user api" //變更描述
},{ //文檔變更記錄,非必須
"version": "1.2", //文檔版本號
"revisionTime": "2022-07-01 22:12:02", //文檔修訂時間
"status": "update", //變更操作狀態,一般爲:創建、更新等
"author": "pdai", //文檔變更作者
"remarks": "add address api" //變更描述
}
],
"customResponseFields": [{ //自定義添加字段和註釋,一般用戶處理第三方jar包庫,非必須
"name": "code",//覆蓋響應碼字段
"desc": "響應代碼",//覆蓋響應碼的字段註釋
"ownerClassName": "org.springframework.data.domain.Pageable", //指定你要添加註釋的類名
"ignore":true, //設置true會被自動忽略掉不會出現在文檔中
"value": "00000"//設置響應碼的值
}],
"requestHeaders": [{ //設置請求頭,沒有需求可以不設置
"name": "token",//請求頭名稱
"type": "string",//請求頭類型
"desc": "desc",//請求頭描述信息
"value":"token請求頭的值",//不設置默認null
"required": false,//是否必須
"since": "-",//什麼版本添加的改請求頭
"pathPatterns": "/app/test/**",//請看https://gitee.com/smart-doc-team/smart-doc/wikis/請求頭高級配置?sort_id=4178978
"excludePathPatterns":"/app/page/**"//請看https://gitee.com/smart-doc-team/smart-doc/wikis/請求頭高級配置?sort_id=4178978
},{
"name": "appkey",//請求頭
"type": "string",//請求頭類型
"desc": "desc",//請求頭描述信息
"value":"appkey請求頭的值",//不設置默認null
"required": false,//是否必須
"pathPatterns": "/test/add,/testConstants/1.0",//正則表達式過濾請求頭,url匹配上纔會添加該請求頭,多個正則用分號隔開
"since": "-"//什麼版本添加的改請求頭
}],
"requestParams": [ //設置公共參數,沒有需求可以不設置
{
"name": "configPathParam",//請求名稱
"type": "string",//請求類型
"desc": "desc",//請求描述信息
"paramIn": "path", // 參數所在位置 header-請求頭, path-路徑參數, query-參數
"value":"testPath",//不設置默認null
"required": false,//是否必須
"since": "2.2.3",//什麼版本添加的該請求
"pathPatterns": "/app/test/**",//請看https://gitee.com/smart-doc-team/smart-doc/wikis/請求高級配置?sort_id=4178978
"excludePathPatterns":"/app/page/**"//請看https://gitee.com/smart-doc-team/smart-doc/wikis/請求高級配置?sort_id=4178978
}],
"responseBodyAdvice":{ //自smart-doc 1.9.8起,非必須項,ResponseBodyAdvice統一返回設置(不要隨便配置根據項目的技術來配置),可用ignoreResponseBodyAdvice tag來忽略
"className":"tech.pdai.springboot.smartdoc.entity.ResponseResult" //通用響應體
}
}
運行測試
可以通過Maven命令生成文檔
//生成html
mvn -Dfile.encoding=UTF-8 smart-doc:html
在IDEA中,也可以通過maven插件構建
maven構建日誌如下
[INFO] Scanning for projects...
[INFO]
[INFO] --------------< tech.pdai:115-springboot-demo-smart-doc >---------------
[INFO] Building 115-springboot-demo-smart-doc 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> smart-doc-maven-plugin:2.4.8:html (default-cli) > compile @ 115-springboot-demo-smart-doc >>>
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ 115-springboot-demo-smart-doc ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 0 resource
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ 115-springboot-demo-smart-doc ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] <<< smart-doc-maven-plugin:2.4.8:html (default-cli) < compile @ 115-springboot-demo-smart-doc <<<
[INFO]
[INFO]
[INFO] --- smart-doc-maven-plugin:2.4.8:html (default-cli) @ 115-springboot-demo-smart-doc ---
[INFO] ------------------------------------------------------------------------
[INFO] Smart-doc Start preparing sources at: 2022-07-01 22:43:54
[INFO] Artifacts that the current project depends on: ["org.springframework.boot:spring-boot-starter-web","org.springframework.boot:spring-boot-configuration-processor","org.projectlombok:lombok"]
[INFO] Smart-doc has loaded the source code path: [{"path":"D:/git/tech-pdai-spring-demos/115-springboot-demo-smart-doc/src/main/java"}]
[INFO] Smart-doc Starting Create API Documentation at: 2022-07-01 22:43:54
[INFO] API documentation is output to => D://md2
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.196 s
[INFO] Finished at: 2022-07-01T22:43:55+08:00
[INFO] ------------------------------------------------------------------------
構建後的html如下:
也可以看到還自動提供了mock的數據,以及測試接口的按鈕。還包含自定義的返回枚舉類型等。
展示效果,可以參看https://api.doubans.com/
生成更多類型的文檔
smart-doc 還支持生成如下類型的文檔:
//生成markdown
mvn -Dfile.encoding=UTF-8 smart-doc:markdown
//生成adoc
mvn -Dfile.encoding=UTF-8 smart-doc:adoc
//生成postman json數據
mvn -Dfile.encoding=UTF-8 smart-doc:postman
// 生成 Open Api 3.0+, Since smart-doc-maven-plugin 1.1.5
mvn -Dfile.encoding=UTF-8 smart-doc:openapi
進一步理解
結合smart-doc官方文檔,我們通過幾個問題進一步理解smart-doc。主要內容來源於官方文檔。
註釋信息是有限的,smart-doc如何從註釋拓展文檔內容呢?
我們知道註釋的信息是有限的,swagger技術棧的方式通過定義註解來約束並拓展文檔中的內容,那麼smart-doc如何從註釋拓展文檔內容呢?
一方面smart-doc
的實現初衷是通過使用javadoc
文檔註釋來去除註解式的侵入,因此smart-doc
每增加一個功能首先都是去考慮javadoc
原生的tag
,
下面對smart-doc
使用的一些javadoc
的註釋tag
做介紹。
tag名稱 | 使用描述 |
---|---|
@param |
對於在Spring Boot 接口層,對於簡單類型的參數必須在使用@param 時寫上註釋描述,對於Entity 類型smart-doc 則不會檢查 |
@deprecated |
可以在註釋中用於標記接口已經廢棄,作用同@Deprecated 註解 |
@apiNote |
@apiNote 是JAVA 新增的文檔tag ,smart-doc 使用@apiNote 的註釋作爲方法的詳細描述,因此可以使用@apiNote 來寫一段長註釋。如果一個方法不寫 @apiNote 註釋說明,smart-doc 直接使用方法默認註釋填充 |
另一方面,原生的tag是不夠的,所以smart-doc
又通過自定義tag來支持更多功能的拓展
tag名稱 | 描述 |
---|---|
@ignore |
@ignore tag 用於過濾請求參數對象上的某個字段,設置後smart-doc 不輸出改字段到請求參數列表中。關於響應字段忽略的請看【忽略響應字段】 如果@ignore 加到方法上,則接口方法不會輸出到文檔。從1.8.4 開始@ignore 支持添加到Controller 上進行忽略不想生成文檔的接口類。@ignore 也可以用於方法上忽略某個請求參數。 |
@required |
如果你沒有使用JSR303 參數驗證規範實現的方式來標註字段,就可以使用@required 去標註請求參數對象的字段,標註smart-doc 在輸出參數列表時會設置爲true 。 |
@mock |
從smart-doc 1.8.0 開始,@mock tag 用於在對象基本類型字段設置自定義文檔展示值。設置值後smart-doc 不再幫你生成隨機值。方便可以通過smart-doc 直接輸出交付文檔。 |
@dubbo |
從smart-doc 1.8.7 開始,@dubbo tag 用於在Dubbo 的API 接口類上添加讓smart-doc 可以掃描到Dubbo RPC 的接口生成文檔。 |
@restApi |
從smart-doc 1.8.8 開始,@restApi tag 用於支持smart-doc 去掃描Spring Cloud Feign 的定義接口生成文檔。 |
@order |
從smart-doc 1.9.4 開始,@order tag 用於設置Controller 接口或者API 入口的自定義排序序號,@order 1 就表示設置序號爲1 。 |
@ignoreResponseBodyAdvice |
從smart-doc 1.9.8 開始,@ignoreResponseBodyAdvice tag 用於忽略ResponseBodyAdvice 設置的包裝類。 |
@download |
從smart-doc 2.0.1 開始,@download tag 用於標註在Controller 的文件下載方法上,生成debug 頁面時可實現文件下載測試。並且支持下載文件帶請求頭參數測試。 |
@page |
從smart-doc 2.0.2 開始,@page tag 用於標註在Controller 的方法上表示該方法用來渲染返回一個靜態頁面,生成debug 頁面時如果發起測試,測試頁面會自動在瀏覽器開啓新標籤顯示頁面。 |
@ignoreParams |
從smart-doc 2.1.0 開始,@ignoreParams tag 用於標註在Controller 方法上忽略掉不想顯示在文檔中的參數,例如:@ignoreParams id name ,多個參數名用空格隔開 |
@response |
從smart-doc 2.2.0 開始,@response tag 標註在Controller 方法上可以允許用這自己定義返回的json example 。建議只在返回基礎類型時使用,如:Result<String> 類型這種泛型是簡單原生類型的響應。 |
@tag |
@since 2.2.5 , @tag 用於將Controller 方法分類, 可以將不同Contoller 下的方法指定到多個分類下, 同時也可以直接指定Controller 爲一個分類或多個分類 |
Maven多模塊中使用插件有沒有比較好的實踐?
在獨立的Maven項目中使用smart-doc,當前可以說是如絲般爽滑。但是在Maven的多模塊項目中使用smart-doc-maven-plugin時,很多同學就有疑問了, smart-doc插件我到底是放在什麼地方合適?是放在Maven的根pom.xml中?還是說各個需要生成API接口文檔的模塊中呢? 下面就來說說根據不同的項目結構應該怎麼放插件。
完全的父子級關係的maven項目:
├─parent
├──common
│ pom.xml
├──web1
│ pom.xml
├──web2
│ pom.xml
└─pom.xml
上面的maven結構假設是嚴格按照父子級來配置的,然後web1和web2都依賴於common, 這種情況下如果跑到web1下或者web2目錄下直接執行mvn命令來編譯 都是無法完成的。需要在根目錄上去執行命令編譯命令才能通過,而smart-doc插件會通過類加載器去加載用戶配置的一些類,因此是需要調用編譯的和執行命令 是一樣的。這種情況下建議你建smart-doc-maven-plugin放到根pom.xml中,在web1和web2中放置各自的smart-doc.json配置。 然後通過-pl去指定讓smart-doc生成指定 模塊的文檔。操作命令如下:
# 生成web1模塊的api文檔
mvn smart-doc:markdown -Dfile.encoding=UTF-8 -pl :web1 -am
# 生成web2模塊的api文檔
mvn smart-doc:markdown -Dfile.encoding=UTF-8 -pl :web2 -am
如果不是按照嚴格父子級構建的項目,還是以上面的結構例子來說。common模塊放在類parent中,但是common的pom.xml並沒有定義parent。 common模塊也很少變更,很多公司內部可能就直接把common單獨depoly上傳到了公司的Nexus倉庫中,這種情況下web1和web2雖然依賴於common, 但是web1和web2都可以在web1和web2目錄下用命令編譯,這種情況下直接將smart-doc-maven-plugin單獨放到web1和web2中是可以做構建生成文檔的。
注意: 怎麼去使用插件並沒有固定的模式,最重要的是熟練Maven的一些列操作,然後根據自己的項目情況來調整。技巧嫺熟就能應對自如。 對於插件的使用,從smart-doc-maven-plugin 1.2.0開始,插件是能夠自動分析生成模塊的依賴來加載必要的源碼,並不會將所有模塊的接口文檔合併到一個文檔中。
如果生成文檔時遇到問題,該如何調試?
在使用
smart-doc-maven-plugin
插件來構建生成API
文檔的過程中可能會出現一些錯誤問題。官方文檔中提供了調試的方案:
- 添加smart-doc依賴
因爲smart-doc-maven-plugin
最終是使用smart-doc
來完成項目的源碼分析和文檔生成的,
通常情況下真正的調試的代碼是smart-doc
。但這個過程主要通過smart-doc-maven-plugin
來排查。
<dependency>
<groupId>com.github.shalousun</groupId>
<artifactId>smart-doc</artifactId>
<version>[最新版本]</version>
<scope>test</scope>
</dependency>
注意: 使用smart-doc
的版本最好和插件依賴的smart-doc
版本一致。
- 添加斷點
添加斷點如圖所示
- Debug模式運行構建目標
maven
插件在idea
中運行debug
非常簡單,操作如下圖。
這樣就可以直接進入斷點了。
提示: 上面是通過插件去作爲入口調試smart-doc
的源碼,如果你想調試插件本身的源碼執行過程,則將插件的依賴添加到項目依賴中,如下:
<dependency>
<groupId>com.github.shalousun</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<version>【maven倉庫最新版本】</version>
</dependency>
然後通過上面的類似步驟調試smart-doc-maven-plugin
的源碼
示例源碼
https://github.com/realpdai/tech-pdai-spring-demos
https://smart-doc-group.github.io/
更多內容
告別碎片化學習,無套路一站式體系化學習後端開發: Java 全棧知識體系(https://pdai.tech)