SpringBoot3整合knife4j(swagger3)的詳細(xì)過程
一、引言
使用jdk21版本創(chuàng)建的spring boot項(xiàng)目, 在SpringBoot3整合swagger3過程中遇到一些問題,網(wǎng)上很多寫的SpringBoot3 整合knife4j(swagger3)的步驟,完全照著做很多都是有問題的。 現(xiàn)在將遇到的問題做記錄。
二、SpringBoot3整合knife4j(swagger3)關(guān)鍵點(diǎn)梳理
1、 添加/修改依賴
- Spring Boot 3.x必須使用
knife4j-openapi3-jakarta(Jakarta命名空間)
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>網(wǎng)上很多文章說使用下面的maven是有問題的。 本人調(diào)試實(shí)際上遇到很多問題,一個(gè)問題解決又出了另一個(gè)問題。
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>- 我用的SpringBoot版本是3.4.5。SpringBoot3.x相比SpringBoot2.x特性上有很大改變。必須選擇SpringBoot3.x版本兼容的knife4j(swagger3)版本。
看看knife4j官網(wǎng)怎么說: https://doc.xiaominfo.com/docs/quick-start

2、添加配置文件
package com.Knife4j;
//
import cn.hutool.core.util.RandomUtil;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class Knife4jConfig {
private final static Logger logger = LoggerFactory.getLogger(Knife4jConfig.class);
private static final String SERVICE_URL = "http://127.0.0.1:8886/tj4/doc.html";
private static final String API_INFO_TITLE = "軟件接口文檔";
private static final String API_INFO_VERSION = "V1.0";
private static final String API_INFO_DESCRIPTION = "Api接口列表";
private static final String API_INFO_LICENSE = "2025年度內(nèi)部文檔,違拷必究.";
private static final String API_INFO_EMAIL = "zpphnkjxy@126.com";
private static final String API_INFO_NAME = "zpp";
// xxx1模塊
@Bean
public GroupedOpenApi api4() {
return GroupedOpenApi.builder()
.group("regularGrade-module-api")
.displayName("平時(shí)成績模塊接口")
.packagesToScan("com.call.controller.regularGrade")//xxx1模塊接口所在包
// 自定義全局響應(yīng)碼
.addOpenApiCustomizer((this::setCustomStatusCode))
.build();
}
// 智能評分模塊
@Bean
public GroupedOpenApi api3() {
return GroupedOpenApi.builder()
.group("IntelligentScoring-module-api")
.displayName("智能評分模塊接口")
.packagesToScan("com.call.controller.intelligentScoring")
// 自定義全局響應(yīng)碼
.addOpenApiCustomizer((this::setCustomStatusCode))
.build();
}
// AI大模型Agent接口
@Bean
public GroupedOpenApi api2() {
return GroupedOpenApi.builder()
.group("aiagent-module-api")
.displayName("AI大模型Agent接口")
.packagesToScan("com.ai.LangChain4j.agent2.DeclarativeAPI")
// .pathsToMatch("/v1/**")
.addOpenApiMethodFilter(method -> method.isAnnotationPresent(io.swagger.v3.oas.annotations.Operation.class))
// 自定義全局響應(yīng)碼
.addOpenApiCustomizer((this::setCustomStatusCode))
.build();
}
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title(API_INFO_TITLE)
.description(API_INFO_DESCRIPTION)
.version(API_INFO_VERSION)
.contact(new Contact().name(API_INFO_NAME).email(API_INFO_EMAIL))
.license(new License().name(API_INFO_LICENSE).url(SERVICE_URL))
);
}
/**
* 設(shè)置自定義錯(cuò)誤碼
*
* @param openApi openApi對象
*/
private void setCustomStatusCode(OpenAPI openApi) {
if (openApi.getPaths() != null) {
Paths paths = openApi.getPaths();
for (Map.Entry<String, PathItem> entry : paths.entrySet()) {
String key = entry.getKey();
PathItem value = entry.getValue();
// put方式自定義全局響應(yīng)碼
Operation put = value.getPut();
// get方式自定義全局響應(yīng)碼
Operation get = value.getGet();
// delete方式自定義全局響應(yīng)碼
Operation delete = value.getDelete();
// post方式自定義全局響應(yīng)碼
Operation post = value.getPost();
if (put != null) {
put.setResponses(handleResponses(put.getResponses()));
}
if (get != null) {
get.setResponses(handleResponses(get.getResponses()));
}
if (delete != null) {
delete.setResponses(handleResponses(delete.getResponses()));
}
if (post != null) {
post.setResponses(handleResponses(post.getResponses()));
}
}
}
}
/**
* 處理不同請求方式中的自定義響應(yīng)碼
* - 響應(yīng)碼中使用原有的響應(yīng)體Content(否則會造成BaseRes中通用的data無法解析各自的對象)
* - 使用原生的ApiResponses作為返回體(否則會造成前端響應(yīng)示例和響應(yīng)內(nèi)容中丟失注釋)
*
* @param responses 響應(yīng)體集合
* @return 返回處理后的響應(yīng)體集合
*/
private ApiResponses handleResponses(ApiResponses responses) {
// 設(shè)置默認(rèn)Content
Content content = new Content();
// 以下代碼注釋,因?yàn)闊o論如何都會從原生responses中獲取到一個(gè)Content
// MediaType mediaType = new MediaType();
// Schema schema = new Schema();
// schema.set$ref("#/components/schemas/BaseRes");
// mediaType.setSchema(schema);
// content.addMediaType("*/*", mediaType);
// 從原來的responses中獲取原生Content
for (Map.Entry<String, ApiResponse> entry : responses.entrySet()) {
String key = entry.getKey();
ApiResponse apiResponse = entry.getValue();
if (apiResponse != null) {
content = apiResponse.getContent();
break;
}
}
// 獲取全部全局響應(yīng)自定義列表
Map<Integer, String> map = StatusCode.toMap();
// 設(shè)置全局響應(yīng)碼
for (Map.Entry<Integer, String> entry : map.entrySet()) {
ApiResponse api = new ApiResponse();
api.setContent(content);
api.description(entry.getValue());
responses.addApiResponse(entry.getKey() + "", api);
}
return responses;
}
/**
* 根據(jù)@Tag 上的排序,寫入x-order
*
* @return the global open api customizer
*/
@Bean
public GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() {
return openApi -> {
if (openApi.getTags()!=null){
openApi.getTags().forEach(tag -> {
Map<String,Object> map=new HashMap<>();
map.put("x-order", RandomUtil.randomInt(0,100));
tag.setExtensions(map);
});
}
if(openApi.getPaths()!=null){
openApi.addExtension("x-test123","333");
openApi.getPaths().addExtension("x-abb",RandomUtil.randomInt(1,100));
}
};
}
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("AI大模型API")
.version("1.0")
.description( "Knife4j集成springdoc-openapi")
.termsOfService("http://doc.xiaominfo.com")
.license(new License().name("Apache 2.0")
.url("http://doc.xiaominfo.com")));
}
}3、啟動(dòng)springboot
啟動(dòng)成功,訪問:http://127.0.0.1:8886/doc.html
出現(xiàn)類似下面的頁面:

4、配置項(xiàng)
springdoc:
# 防止全局異常處理器的響應(yīng)定義覆蓋所有接口[citation:2]
override-with-generic-response: false
# 禁用損壞引用清理(如遇到Schema解析問題可嘗試)[citation:4]
remove-broken-reference-definitions: false
swagger-ui:
path: /doc.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: 'default'
paths-to-match: '/**'
packages-to-scan: com
# knife4j的增強(qiáng)配置,不需要增強(qiáng)可以不配
knife4j:
enable: true
setting:
language: zh_cn運(yùn)行結(jié)果(目前訪問doc.html,會自動(dòng)跳轉(zhuǎn)到swagger-ui/index.html):

5、配置項(xiàng)中添加更多控制細(xì)節(jié)
配置項(xiàng)如下:
# 配置Knife4j,以啟用Swagger文檔的增強(qiáng)功能和定制化展示
knife4j:
# 啟用Knife4j擴(kuò)展
enable: true
# 配置展示的文檔分組
documents:
-
# 文檔分組標(biāo)題
group: 2.X版本
# 文檔分組描述
name: 接口簽名
# 指定接口文檔的位置
locations: classpath:sign/*
# 配置Knife4j的展示細(xì)節(jié)和功能開關(guān)
setting:
# 設(shè)置界面語言
language: zh-CN
# 啟用Swagger模型展示
enable-swagger-models: true
# 啟用文檔管理功能
enable-document-manage: true
# 設(shè)置Swagger模型的顯示名稱
swagger-model-name: 實(shí)體類列表
# 是否顯示版本信息
enable-version: false
# 是否啟用參數(shù)緩存刷新
enable-reload-cache-parameter: false
# 啟用后端腳本支持
enable-after-script: true
# 過濾特定方法類型的multipart/form-data接口
enable-filter-multipart-api-method-type: POST
# 是否過濾所有multipart/form-data類型的接口
enable-filter-multipart-apis: false
# 啟用請求緩存
enable-request-cache: true
# # 是否顯示自定義主機(jī)名
# enable-host: false
# # 設(shè)置自定義的主機(jī)名
# enable-host-text: 192.168.0.193:8000
# # 啟用自定義首頁
# enable-home-custom: true
# # 設(shè)置自定義首頁的路徑
# home-custom-path: classpath:markdown/home.md
# 是否啟用搜索功能
enable-search: false
# 是否顯示頁腳
enable-footer: false
# 啟用自定義頁腳內(nèi)容
enable-footer-custom: true
# 設(shè)置自定義頁腳的內(nèi)容
footer-custom-content: Apache License 2.0
# 是否啟用動(dòng)態(tài)參數(shù)
enable-dynamic-parameter: false
# 啟用調(diào)試模式
enable-debug: true
# 啟用OpenAPI 3.0的支持
enable-open-api: false
# 啟用接口分組功能
enable-group: true
# 是否啟用CORS跨域支持
cors: false
# 是否啟用生產(chǎn)模式
production: false
# 配置基本的認(rèn)證信息
basic:
# 啟用基本認(rèn)證
enable: false
# 設(shè)置用戶名
username: admin
# 設(shè)置密碼
password: 123
springdoc:
# 防止全局異常處理器的響應(yīng)定義覆蓋所有接口[citation:2]
override-with-generic-response: false
# 禁用損壞引用清理(如遇到Schema解析問題可嘗試)[citation:4]
remove-broken-reference-definitions: false
swagger-ui:
path: /doc.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: 'default'
paths-to-match: '/**'
packages-to-scan: com目前啟動(dòng)時(shí)會報(bào)如下錯(cuò)誤(暫時(shí)未解決):
2025-12-24T23:04:56.337+08:00 WARN 35067 --- [langchain4jAI] [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'knife4jAutoConfiguration' defined in URL [jar:file:/Users/apple/.m2/repository/com/github/xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter/4.5.0/knife4j-openapi3-jakarta-spring-boot-starter-4.5.0.jar!/com/github/xiaoymin/knife4j/spring/configuration/Knife4jAutoConfiguration.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties' available: expected single matching bean but found 2: knife4jProperties,knife4j-com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties 2025-12-24T23:04:56.444+08:00 INFO 35067 --- [langchain4jAI] [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat] 2025-12-24T23:04:56.478+08:00 INFO 35067 --- [langchain4jAI] [ restartedMain] .s.b.a.l.ConditionEvaluationReportLogger : Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2025-12-24T23:04:56.762+08:00 ERROR 35067 --- [langchain4jAI] [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: Parameter 0 of constructor in com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration required a single bean, but 2 were found: - knife4jProperties: defined in URL [jar:file:/Users/apple/.m2/repository/com/github/xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter/4.5.0/knife4j-openapi3-jakarta-spring-boot-starter-4.5.0.jar!/com/github/xiaoymin/knife4j/spring/configuration/Knife4jProperties.class] - knife4j-com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties: defined in unknown location This may be due to missing parameter name information Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed Ensure that your compiler is configured to use the '-parameters' flag. You may need to update both your build tool settings as well as your IDE. (See https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x#parameter-name-ret
等等
三、問題梳理
問題1: 啟動(dòng)springboot后訪問doc.html頁面報(bào)錯(cuò)
java.lang.NoSuchMethodError: 'void org.springframework.web.method.ControllerAdviceBean.<init>(java.lang.Object)'] with root cause java.lang.NoSuchMethodError: 'void org.springframework.web.method.ControllerAdviceBean.<init>(java.lang.Object)' at org.springdoc.core.service.GenericResponseService.lambda$getGenericMapResponse$8(GenericResponseService.java:702) ~[springdoc-openapi-starter-common-2.3.0.jar:2.3.0] at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178) ~[na:na] at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024) ~[na:na] at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[na:na] at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na] at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575) ~[na:na] at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260) ~[na:na] at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616) ~[na:na] at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622) ~[na:na] at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627) ~[na:na] at org.springdoc.core.service.GenericResponseService.getGenericMapResponse(GenericResponseService.java:704) ~[springdoc-openapi-starter-common-2.3.0.jar:2.3.0] at org.springdoc.core.service.GenericResponseService.build(GenericResponseService.java:246) ~[springdoc-openapi-starter-common-2.3.0.jar:2.3.0] at org.springdoc.api.AbstractOpenApiResource.calculatePath(AbstractOpenApiResource.java:499) ~[springdoc-openapi-starter-common-2.3.0.jar:2.3.0]
網(wǎng)上查了很多資料,最終看到下面的兩個(gè)內(nèi)容解決了問題:
http://www.fzitv.net/article/223113.htm
http://www.fzitv.net/program/353796sg3.htm
由于我不想降低springboot的版本, 故而選擇修改配置文件。 最終解決辦法: 確定該問題是增加異常處理器類后,對全局的異常起作用了,如對swagger也起作用了。所以可以在application配置里增加springdoc的配置,防止全局異常處理器的響應(yīng)定義覆蓋所有接口。
application.yml添加的內(nèi)容如下所示:
springdoc: # 防止全局異常處理器的響應(yīng)定義覆蓋所有接口[citation:2] override-with-generic-response: false # 禁用損壞引用清理(如遇到Schema解析問題可嘗試)[citation:4] remove-broken-reference-definitions: false
到此這篇關(guān)于SpringBoot3整合knife4j(swagger3)的詳細(xì)過程的文章就介紹到這了,更多相關(guān)SpringBoot3整合knife4j內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringSecurity6.x多種登錄方式配置小結(jié)
SpringSecurity6.x變了很多寫法,本文就來介紹一下SpringSecurity6.x多種登錄方式配置小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
idea自動(dòng)加載html、js而無需重啟進(jìn)程的操作
這篇文章主要介紹了idea自動(dòng)加載html、js而無需重啟進(jìn)程的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
Java 數(shù)據(jù)結(jié)構(gòu)哈希算法之哈希桶方式解決哈希沖突
實(shí)際上哈希桶是解決哈希表沖突的一種方法。常見的解決沖突的兩種方法:分離鏈接法、開放定址法。其中使用分離鏈接法,得到的對應(yīng)關(guān)系即為哈希桶2022-02-02
@AutoConfigurationPackage與@ComponentScan注解區(qū)別
這篇文章主要介紹了@AutoConfigurationPackage與@ComponentScan注解區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
Java動(dòng)態(tài)代理靜態(tài)代理實(shí)例分析
這篇文章主要介紹了Java動(dòng)態(tài)代理靜態(tài)代理實(shí)例分析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
Java中的 AtomicReference類概覽及實(shí)現(xiàn)方案
在Java開發(fā)中,常常需要構(gòu)建無鎖的應(yīng)用程序,以實(shí)現(xiàn)高性能的并發(fā)控制,本文給大家介紹Java中的AtomicReference類概覽及實(shí)現(xiàn)方案,感興趣的朋友跟隨小編一起看看吧2025-09-09
Java形參和實(shí)參的實(shí)例之Integer類型與Int類型用法說明
這篇文章主要介紹了Java形參和實(shí)參的實(shí)例之Integer類型與Int類型用法說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
java高并發(fā)InterruptedException異常引發(fā)思考
這篇文章主要為大家介紹了java高并發(fā)InterruptedException異常引發(fā)思考,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08

