優(yōu)雅哥 SpringBoot 2.7 實(shí)戰(zhàn)基礎(chǔ) - 04 -集成 Spring Doc 接口文檔和 knife4j
前面已經(jīng)集成 MyBatis Plus、Druid 數(shù)據(jù)源,開(kāi)發(fā)了 5 個(gè)接口。在測(cè)試這 5 個(gè)接口時(shí)使用了 HTTP Client 或 PostMan,無(wú)論是啥都比較麻煩:得自己寫(xiě)請(qǐng)求地址 URL、請(qǐng)求參數(shù)等,于是多年前就出現(xiàn)了 Swagger 這個(gè)玩意。Swagger 可以自動(dòng)生成接口文檔,還能很方便的測(cè)試各個(gè)接口。但不幸的是,MVN Repository 上面 Springfox Swagger2 的版本停止于 2020 年 7月,而寫(xiě)下這篇文章是 2022 年 8 月,已經(jīng)兩年過(guò)去沒(méi)有動(dòng)靜了,與此同時(shí),springdoc-openapi 悄然出現(xiàn)。
spring doc open api 支持 Open API 3、Swagger-ui等,可以很方便與 Spring Boot 整合,配置和使用與 Springfox Swagger2 類(lèi)似。
1 集成 Spring Doc
1.1 添加依賴(lài)
springdoc-openapi 不是 Spring Framework 官方團(tuán)隊(duì)開(kāi)發(fā)的,而是社區(qū)項(xiàng)目,沒(méi)有包含在 spring-boot-dependencies 中。故需要先定義版本號(hào):
<properties>
....
<springdoc-openapi-ui.version>1.6.9</springdoc-openapi-ui.version>
</properties>
添加依賴(lài):
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${springdoc-openapi-ui.version}</version>
</dependency>
該依賴(lài)?yán)锩媸褂昧?swagger-ui,以HTML形式展示文檔。
1.2 編寫(xiě)配置類(lèi)
其實(shí)配置類(lèi)寫(xiě)不寫(xiě)都可以,如果不寫(xiě)配置類(lèi),就通過(guò)注解定義文檔信息就可以。創(chuàng)建類(lèi):com.yygnb.demo.config.SpringDocConfig
package com.yygnb.demo.config;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringDocConfig {
private String title = "Hero SpringBoot Demo";
private String description = "Hero Demo for usage of Spring Boot";
private String version = "v0.0.1";
private String websiteName = "Hero Website";
private String websiteUrl = "http://www.yygnb.com";
@Bean
public OpenAPI heroOpenAPI() {
return new OpenAPI()
.info(new Info().title(title)
.description(description)
.version(version))
.externalDocs(new ExternalDocumentation().description(websiteName)
.url(websiteUrl));
}
}
上面的配置定義了展示的文檔的信息,與界面上對(duì)應(yīng)關(guān)系如下:

在配置文件中除了可以配置文檔的信息,還可以配置文檔分組、Authorization 等,在后面的企業(yè)級(jí)實(shí)戰(zhàn)文章中會(huì)具體描寫(xiě),將會(huì)在網(wǎng)關(guān)層 spring cloud gateway 中集成所有微服務(wù)的接口。
1.3 配置yml
在 application.yml 配置 springdoc:
# 接口文檔
springdoc:
packages-to-scan: com.yygnb.demo.controller
swagger-ui:
enabled: true
這兩項(xiàng)不配置也可以,packages-to-scan 默認(rèn)為啟動(dòng)類(lèi)所在的路徑;springdoc.swagger-ui.enabled 默認(rèn)為true,配置后可以在不同的環(huán)境中開(kāi)啟或關(guān)閉。
1.4 添加注解
springdoc-openapi 與 springfox-swagger2 提供的注解有很大差別:
| swagger 2 | spring doc | 描述 |
|---|---|---|
| @Api | @Tag | 修飾 controller 類(lèi),類(lèi)的說(shuō)明 |
| @ApiOperation | @Operation | 修飾 controller 中的接口方法,接口的說(shuō)明 |
| @ApiModel | @Schema | 修飾實(shí)體類(lèi),該實(shí)體的說(shuō)明 |
| @ApiModelProperty | @Schema | 修飾實(shí)體類(lèi)的屬性,實(shí)體類(lèi)中屬性的說(shuō)明 |
| @ApiImplicitParams | @Parameters | 接口參數(shù)集合 |
| @ApiImplicitParam | @Parameter | 接口參數(shù) |
| @ApiParam | @Parameter | 接口參數(shù) |
修改實(shí)體類(lèi) Computer,添加 springdoc-openapi 注解:
@Schema(title = "電腦")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Computer implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(title = "尺寸")
private BigDecimal size;
@Schema(title = "操作系統(tǒng)")
private String operation;
@Schema(title = "年份")
private String year;
}
修改控制器 ComputerController,添加注解:
@Tag(name = "電腦相關(guān)接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("/computer")
public class ComputerController {
private final IComputerService computerService;
@Operation(summary = "根據(jù)id查詢(xún)電腦")
@GetMapping("/{id}")
public Computer findById(
@Parameter(name = "id", required = true, description = "電腦id") @PathVariable Long id) {
return this.computerService.getById(id);
}
@Operation(summary = "分頁(yè)查詢(xún)電腦列表")
@Parameters(value = {
@Parameter(name = "page", description = "頁(yè)面,從1開(kāi)始", example = "2"),
@Parameter(name = "size", description = "每頁(yè)大小", example = "10")
})
@GetMapping("/find-page/{page}/{size}")
public Page<Computer> findPage(@PathVariable Integer page, @PathVariable Integer size) {
return this.computerService.page(new Page<>(page, size));
}
@Operation(summary = "新增電腦")
@PostMapping()
public Computer save(@RequestBody Computer computer) {
computer.setId(null);
this.computerService.save(computer);
return computer;
}
@Operation(summary = "根據(jù)id修改電腦")
@PutMapping("/{id}")
public Computer update(
@Parameter(name = "id", required = true, description = "電腦id") @PathVariable Long id,
@RequestBody Computer computer) {
computer.setId(id);
this.computerService.updateById(computer);
return computer;
}
@Operation(summary = "根據(jù)id刪除電腦")
@DeleteMapping("/{id}")
@Parameter(name = "id", required = true, description = "電腦id")
public void delete(@PathVariable Long id) {
this.computerService.removeById(id);
}
}
1.5 運(yùn)行測(cè)試
啟動(dòng)服務(wù),在瀏覽器中訪(fǎng)問(wèn):
http://localhost:9099/swagger-ui/index.html

2 api-docs
在文檔標(biāo)題下面有一個(gè)小鏈接:/v3/api-docs,點(diǎn)擊該鏈接,會(huì)在新頁(yè)面中顯示一大坨 JSON 數(shù)據(jù)。

看似很無(wú)聊的數(shù)據(jù),卻有著重大意義,swagger-ui 就是通過(guò)這些數(shù)據(jù)渲染出頁(yè)面的。此外,這些JSON數(shù)據(jù)在某種程度上可以簡(jiǎn)化前端的開(kāi)發(fā)及前后端網(wǎng)絡(luò)請(qǐng)求的工作量。
hero-admin-ui
優(yōu)雅哥正在開(kāi)發(fā)的基于 Vue 3 + TypeScript 的開(kāi)源項(xiàng)目 hero-admin-ui,其特色就是基于 JSON Schema 的表單和列表。通過(guò) JSON Schema 可以快速渲染出一個(gè)列表、表單,甚至是搜索頁(yè)和詳情表單頁(yè)。如果獨(dú)立使用 hero-admin-ui,需要手動(dòng)編寫(xiě) JSON Schema,但如果后端接口整合了 Swagger 或 Spring Doc,上面 api-docs 返回的 JSON,就包含了 JSON Schema,二者結(jié)合可以快速實(shí)現(xiàn)搜索頁(yè)、表單頁(yè)等。目前 hero-admin-ui 已發(fā)布在 npmjs 上,也已經(jīng)提交到 github上,大家可以搜索關(guān)鍵字 hero-admin-ui 查看。在后面的實(shí)戰(zhàn)篇中,前端部分將會(huì)使用這個(gè)組件庫(kù)實(shí)現(xiàn)前端頁(yè)面。

3 自定義配置
在 ”1.2 編寫(xiě)配置類(lèi)“ 一節(jié),文檔信息都是寫(xiě)死在代碼中的,如果多個(gè)微服務(wù)都要集成 spring doc,可以把前面寫(xiě)的 SpringDocConfig 提取到公共模塊中,通過(guò)maven 依賴(lài)引用,在 application.yml 中配置不同的變量。
3.1 添加依賴(lài)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
配置該依賴(lài)的目的是在編寫(xiě) application.yml 時(shí),自定義的屬性有代碼提示。它會(huì)生成配置元數(shù)據(jù),無(wú)需自己手動(dòng)編寫(xiě)。
3.2 定義配置的實(shí)體類(lèi)
創(chuàng)建 com.yygnb.demo.config.DocInfo,將 SpringDocConfig 中寫(xiě)死的屬性都移到這個(gè)配置實(shí)體類(lèi)中:
@Data
@Component
@ConfigurationProperties(prefix = "doc-info")
public class DocInfo {
private String title = "Demo Title";
private String description = "Demo Description";
private String version = "v0.0.1";
private String websiteName = "Demo Website";
private String websiteUrl = "http://www.yygnb.com";
}
注解 @ConfigurationProperties(prefix = "doc-info") 聲明配置屬性,在 application.yml 配置時(shí)就可以使用 doc-info。
3.3 重構(gòu) SpringDocConfig
在 SpringDocConfig 中引入 DocInfo,并通過(guò)構(gòu)造函數(shù)進(jìn)行注入:
@RequiredArgsConstructor
@Configuration
public class SpringDocConfig {
private final DocInfo docInfo;
@Bean
public OpenAPI springShopOpenAPI() {
return new OpenAPI()
.info(new Info().title(docInfo.getTitle())
.description(docInfo.getDescription())
.version(docInfo.getVersion()))
.externalDocs(new ExternalDocumentation().description(docInfo.getWebsiteName())
.url(docInfo.getWebsiteUrl()));
}
}
補(bǔ)充一個(gè)小點(diǎn):注解 @RequiredArgsConstructor 是 lombok 中提供的,它等價(jià)于在類(lèi)中編寫(xiě)了方法:
public SpringDocConfig(DocInfo docInfo) {
this.docInfo = docInfo;
}
構(gòu)造注入時(shí),在構(gòu)造函數(shù)中寫(xiě)大量的屬性,毫無(wú)意義。既然已經(jīng)使用了@Data 注解,為啥不用 @RequiredArgsConstructor 呢?
3.4 使用自定義配置
自定義配置已經(jīng)完成,可以在 application.yml 中使用 DocInfo 對(duì)應(yīng)的配置了:
doc-info:
title: SpringBoot Demo演示
description: 學(xué)習(xí) Spring Boot 2.7.2
DocInfo 所有屬性都定義了默認(rèn)值,在 application.yml 可以覆蓋默認(rèn)值,如上面的 title 和 description 屬性。重啟服務(wù)查看運(yùn)行效果:

4 集成 knife4j
在之前 springfox-swagger 的時(shí)代,很多同學(xué)不喜歡 swagger-ui 的界面風(fēng)格,會(huì)集成 knife4j 的 ui。Spring Doc 也可以集成 knife4j。
如果要使用 knife4j ,Spring Doc 的配置中需要添加分組配置,我們這里添加一個(gè)最簡(jiǎn)單的分組配置。
com.yygnb.demo.config.SpringDocConfig
@RequiredArgsConstructor
@Configuration
public class SpringDocConfig {
private final DocInfo docInfo;
@Bean
public OpenAPI heroOpenAPI() {
return new OpenAPI()
.info(new Info().title(docInfo.getTitle())
.description(docInfo.getDescription())
.version(docInfo.getVersion()))
.externalDocs(new ExternalDocumentation().description(docInfo.getWebsiteName())
.url(docInfo.getWebsiteUrl()));
}
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group(docInfo.getTitle())
.pathsToMatch("/**")
.build();
}
}
如果不添加這個(gè) GroupedOpenApi 實(shí)例,knife4j ui就顯示不出來(lái)。
在 pom.xml 中引入 knife4j
<properties>
...
<knife4j-springdoc-ui.version>3.0.3</knife4j-springdoc-ui.version>
</properties>
<dependencies>
...
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-springdoc-ui</artifactId>
<version>${knife4j-springdoc-ui.version}</version>
</dependency>
</dependencies>
啟動(dòng)服務(wù),訪(fǎng)問(wèn):
http://localhost:9099/doc.html
顯示 knife4j 的ui:

今日優(yōu)雅哥(工\/youyacoder)學(xué)習(xí)結(jié)束,期待關(guān)注留言分享~~