SpringBoot v2.7.x+ 整合Swagger3入坑记?
目录
一、依赖
二、集成Swagger Java Config
三、配置完毕
四、解决方案
彩蛋
想尝鲜,坑也多,一起入个坑~
一、依赖
SpringBoot版本:2.7.14
Swagger版本:3.0.0
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version>
</dependency>
二、集成Swagger Java Config
@Value("${server.port:8080}")
private String port;@Value("${server.servlet.context-path:}")
private String rootPath;@Bean
Docket docket(SwaggerProperties properties) {Docket docket = new Docket(DocumentationType.OAS_30).apiInfo(apiInfo(properties)).groupName(properties.getGroupName()).select().apis(scanBasePackages(properties.getBasePackage())).paths(PathSelectors.any()).build().globalRequestParameters(globalRequestParameters(properties)).globalResponses(HttpMethod.POST, responses()).globalResponses(HttpMethod.GET, responses()).pathMapping("/");log.info("Swagger3 successfully started: http://{}:{}{}/doc.html", IPUtils.getLocalIP(), port, rootPath);return docket;
}@Bean
public ModelPropertyBuilderPlugin modelPropertyBuilderPlugin() {return new DictPropertyPlugin();
}/*** 构建响应状态码*/
private List<Response> responses() {List<Response> responses = new LinkedList<>();responses.add(new ResponseBuilder().code("S").description("响应成功").build());responses.add(new ResponseBuilder().code("E").description("非'S'即为响应失败").build());return responses;
}private ApiInfo apiInfo(SwaggerProperties properties) {return new ApiInfoBuilder().title(properties.getTitle()).description(properties.getDescription()).license(properties.getLicense()).licenseUrl(properties.getLicenseUrl()).termsOfServiceUrl(properties.getTermsOfServiceUrl()).contact(new Contact(properties.getContact().getName(), properties.getContact().getUrl(), properties.getContact().getEmail())).version(properties.getVersion()).build();
}/*** 自定义请求参数** @return - list*/
private List<RequestParameter> globalRequestParameters(SwaggerProperties properties) {List<RequestParameter> params = new ArrayList<>();properties.getParams().forEach(e -> {RequestParameter parameter = new RequestParameterBuilder().name(e.getName()).description(e.getDesc()).required(e.isRequired()).in(e.getParamType()).hidden(e.isHidden()).build();params.add(parameter);});return params;
}/*** 多包扫描支持,扫描的包生成{@linkplain Predicate < RequestHandler >}** @param basePackages - 扫描的包*/
private Predicate<RequestHandler> scanBasePackages(final String... basePackages) {if (basePackages == null || basePackages.length == 0) {throw new IllegalArgumentException("basePackages不能为空");}Predicate<RequestHandler> predicate = null;for (int i = basePackages.length - 1; i >= 0; i--) {String strBasePackage = basePackages[i];if (StrUtil.isNotBlank(strBasePackage)) {Predicate<RequestHandler> tempPredicate = RequestHandlerSelectors.basePackage(strBasePackage);predicate = predicate == null ? tempPredicate : predicate.or(tempPredicate);}}if (predicate == null) {throw new IllegalArgumentException("basePackage配置不正确");}return predicate;
}
/*** swagger3 自定义展示枚举类型信息*/
public class DictPropertyPlugin implements ModelPropertyBuilderPlugin {private final Logger log = LoggerFactory.getLogger(getClass());@Overridepublic void apply(ModelPropertyContext ctx) {Optional<BeanPropertyDefinition> opt = ctx.getBeanPropertyDefinition();opt.ifPresent(bean -> {Class<?> cls = bean.getRawPrimaryType();if (IDict.class.isAssignableFrom(cls) && Enum.class.isAssignableFrom(cls)) {if (cls.getEnumConstants() == null) {return;}try {Field f = PropertySpecificationBuilder.class.getDeclaredField("description");f.setAccessible(true);String prefix = cls.getSimpleName() + "(" + f.get(ctx.getSpecificationBuilder()) + ")【";StringJoiner join = new StringJoiner(",", prefix, "】");for (IDict<?, ?> d : (IDict<?, ?>[]) cls.getEnumConstants()) {join.add(d.getDesc() + "[" + d.getCode() + "]-" + ((Enum<?>) d).name());}ctx.getSpecificationBuilder().description(join.toString());} catch (Exception e) {log.error("字典值处理失败:{}", cls.getName(), e);}}});}@Overridepublic boolean supports(DocumentationType type) {return DocumentationType.OAS_30.equals(type);}
}
properties
package com.muchenx.common.swagger.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import springfox.documentation.service.ParameterType;import java.util.ArrayList;
import java.util.List;@ConfigurationProperties(prefix = "swagger")
public class SwaggerProperties {/*** swagger会解析的包路径**/private String basePackage = "com.muchenx";/*** 分组名*/private String groupName = "default";/*** 标题**/private String title = "MuchenX";/*** 描述**/private String description = "MuchenX Cloud Project supports by Spring Cloud Alibaba";/*** 版本**/private String version = "v1.0";/*** 许可证**/private String license = "";/*** 许可证URL**/private String licenseUrl = "";/*** 服务条款URL**/private String termsOfServiceUrl = "";/*** host信息**/private String host = "";/*** 联系人信息*/private Contact contact = new Contact();/*** 自定义参数*/private List<Param> params = new ArrayList<>();public String getGroupName() {return groupName;}public void setGroupName(String groupName) {this.groupName = groupName;}public String getBasePackage() {return basePackage;}public void setBasePackage(String basePackage) {this.basePackage = basePackage;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getLicense() {return license;}public void setLicense(String license) {this.license = license;}public String getLicenseUrl() {return licenseUrl;}public void setLicenseUrl(String licenseUrl) {this.licenseUrl = licenseUrl;}public String getTermsOfServiceUrl() {return termsOfServiceUrl;}public void setTermsOfServiceUrl(String termsOfServiceUrl) {this.termsOfServiceUrl = termsOfServiceUrl;}public String getHost() {return host;}public void setHost(String host) {this.host = host;}public Contact getContact() {return contact;}public void setContact(Contact contact) {this.contact = contact;}public List<Param> getParams() {return params;}public void setParams(List<Param> params) {this.params = params;}public static class Contact {/*** 联系人**/private String name = "Ian Geng";/*** 联系人url**/private String url = "www.muchenx.com";/*** 联系人email**/private String email = "gzhygz@gmail.com";public String getName() {return name;}public void setName(String name) {this.name = name;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}}public static class Param {// 请求参数名private String name;// 请求参数描述private String desc;/*** 请求参数类型:QUERY("query"),HEADER("header"),PATH("path"),* COOKIE("cookie"),FORM("form"),FORMDATA("formData"),BODY("body");*/private ParameterType paramType = ParameterType.HEADER;// 请求参数默认值private String defaultValue = "";// 是否必填private boolean required = false;// 是否隐藏private boolean hidden = false;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public ParameterType getParamType() {return paramType;}public void setParamType(ParameterType paramType) {this.paramType = paramType;}public String getDefaultValue() {return defaultValue;}public void setDefaultValue(String defaultValue) {this.defaultValue = defaultValue;}public boolean isRequired() {return required;}public void setRequired(boolean required) {this.required = required;}public boolean isHidden() {return hidden;}public void setHidden(boolean hidden) {this.hidden = hidden;}}
}
三、配置完毕
在启动类增加注解开起swagger:@springfox.documentation.oas.annotations.EnableOpenApi
此时控制台报错
Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is nullat springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113) ~[springfox-core-3.0.0.jar:3.0.0]at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89) ~[springfox-spi-3.0.0.jar:3.0.0]at java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:473) ~[na:na]at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) ~[na:na]at java.base/java.util.TimSort.sort(TimSort.java:220) ~[na:na]at java.base/java.util.Arrays.sort(Arrays.java:1307) ~[na:na]at java.base/java.util.ArrayList.sort(ArrayList.java:1721) ~[na:na]at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:392) ~[na:na]at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na]at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) ~[na:na]at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) ~[na:na]...........
原因是:主要出现在Spring Boot 2.6及以后,只要是Spring Boot 2.6引入的新PathPatternParser导致的。
四、解决方案
spring官方提及此issue:because "this.condition" is null · Issue #28794 · spring-projects/spring-boot · GitHub
但尚未解决,issue已关闭。
springfox社区活跃,已有大神解决此问题:Spring 5.3/Spring Boot 2.4 support · Issue #3462 · springfox/springfox · GitHub
适合的方案如下

1.Path匹配策略切换回ant_path_matcher(大多情况此方案可解决)
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
2.若还是不能解决,添加如下配置
@Bean
ic WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {List<ExposableEndpoint<?>> allEndpoints = new ArrayList();Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();allEndpoints.addAll(webEndpoints);allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());String basePath = webEndpointProperties.getBasePath();EndpointMapping endpointMapping = new EndpointMapping(basePath);boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
涉及依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator</artifactId><version>2.7.14</version><scope>compile</scope>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator-autoconfigure</artifactId><version>2.7.14</version><scope>compile</scope>
</dependency>
配置完重启服务问题解决!
彩蛋
swagger3与springboot完整的集成方案已上架,欢迎查收:彩蛋~
相关文章:
SpringBoot v2.7.x+ 整合Swagger3入坑记?
目录 一、依赖 二、集成Swagger Java Config 三、配置完毕 四、解决方案 彩蛋 想尝鲜,坑也多,一起入个坑~ 一、依赖 SpringBoot版本:2.7.14 Swagger版本:3.0.0 <dependency><groupId>com.github.xiaoymin<…...
说说你了解的 CDC
分析&回答 什么是 CDC CDC,Change Data Capture,变更数据获取的简称,使用CDC我们可以从数据库中获取已提交的更改并将这些更改发送到下游,供下游使用。这些变更可以包括INSERT,DELETE,UPDATE等。用户可以在以下的场景下使用CDC: 使用f…...
SpingMvc入门
SpingMvc入门 1.MVC Spring的工作流程:2.sping mvc入门3.静态资源处理 前言 Spring MVC是一种基于Java的web应用开发框架,它采用了MVC(Model-View-Controller)设计模式来帮助开发者组织和管理应用程序的各个组件。 1.MVC Spring的…...
JVM的故事——类文件结构
类文件结构 文章目录 类文件结构一、概述二、无关性基石三、Class类文件的结构 一、概述 计算机是只认由0、1组成的二进制码的,不过随着发展,我们编写的程序可以被编译成与指令集无关、平台中立的一种格式。 二、无关性基石 对于不同平台和不同平台的…...
springboot自定义表格(动态合并单元格)
一、需求展示(一个订单多个商品,商品数量不限订单行合并) 二、技术选型(jxls自定义模板) <!-- 版本具体看官网Release,这里我们使用 2.13.0 --><dependency><groupId>org.jxls</group…...
C++零碎记录(二)
3. 调用其他类 3.1 类中有其他的类 #include <iostream> using namespace std;//点和圆关系案例//点类 class Point { public://设置xvoid setX(int x){m_X x;}//获取xint getX(){return m_X;}//设置yvoid setY(int y){m_Y y;}//获取yint getY(){return m_Y;}private…...
数学建模:回归分析
🔆 文章首发于我的个人博客:欢迎大佬们来逛逛 数学建模:回归分析 文章目录 数学建模:回归分析回归分析多元线性回归案例 多项式回归一元多项式回归多元二项式回归 非线性回归逐步回归 回归分析 多元线性回归 案例 首先进行回归分…...
数据库(一)
数据库 1.为什么要使用数据库 如果要存储数据,我们是可以使用文件来存储数据的,但是使用文件管理数据有很多缺点,比如: 不安全,不利于管理,查询,如果要存储大量的数据,使用文件管理…...
【算法与数据结构】106、LeetCode从中序与后序遍历序列构造二叉树
文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:首先我们要知道后序遍历数组的最后一个元素必然是根节点,然后根据根节点在中序遍历数组中的…...
kali 安装cpolar内网穿透实现 ssh 远程连接
文章目录 1. 启动kali ssh 服务2. kali 安装cpolar 内网穿透3. 配置kali ssh公网地址4. 远程连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 简单几步通过cpolar 内网穿透软件实现ssh 远程连接kali! 1. 启动kali ssh 服务 默认新安装的kali系统会关闭ssh 连接服务,我们通…...
算法训练 第一周
一、合并两个有序数组 本题给出了两个整数数组nums1和nums2,这两个数组均是非递减排列,要求我们将这两个数组合并成一个非递减排列的数组。题目中还要求我们把合并完的数组存储在nums1中,并且为了存储两个数组中全部的数据,nums1中…...
软件评测师之码制
目录 一、机器数二、码制三、数的表示范围 一、机器数 机器数就是一个数在计算机中的二进制表示,计算机中机器数的最高位是符号位,正数符号位为0,负数符号位为1,机器数包含原码、反码和补码三种表示形式。 二、码制 表现形式数…...
ubuntu18安装cmake27的方法
背景是ubuntu18默认的cmake是3.10 $ apt search cmake Sorting... Done Full Text Search... Done bear/bionic,bionic 2.3.11-1 allgenerate compilation database for Clang toolingcatkin/bionic,bionic 0.7.8-1 allLow-level build system macros and infrastructure for …...
通讯编程006——NodeJS OPC UA Client开发简单教程
本文介绍如何在NodeJS环境下开发OPC UA Client,通过本文可以对OPC UA的基本概念有所了解,掌握OPC UA的本质。相关软件请登录网信智汇(wangxinzhihui.com)。 开发步骤如下: 1)首先需要安装nodejs,要求版本至少是12。 …...
「高等数学」雅可比矩阵和黑塞矩阵的异同
「高等数学」雅可比矩阵和黑塞矩阵的异同 雅可比矩阵,Jacobi matrix 或者 Jacobian,是向量值函数( f : R n → R m f:\mathbb{R}^n \to \mathbb{R}^m f:Rn→Rm)的一阶偏导数按行排列所得的矩阵。 黑塞矩阵,又叫海森矩…...
继承(个人学习笔记黑马学习)
1、基本语法 #include <iostream> using namespace std; #include <string>//普通实现页面//Java页面 //class Java { //public: // void header() { // cout << "首页、公开课、登录、注册...(公共头部)" << endl; // } // void footer() …...
ToBeWritten之ATTCK 测评方案
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 转移发布平台通知:将不再在CSDN博客发布新文章,敬…...
JSONUtil详解
JSONUtil是一个通用的JSON工具类,用于在Java中操作JSON数据。虽然之前提到的示例中没有直接提及JSONUtil,但可以解释一下可能存在的一些常见JSON操作方法,这些方法通常可以在不同的JSON工具类中找到。 JSONUtil中的一些常见方法包括…...
ArcGIS Maps SDK for JS(一):概述与使用
文章目录 1 概述2 如何使用ArcGIS Maps SDK for JavaScript2.1 AMD 模块与 ES 模块2.2 AMD 模块和 ES 模块比较 3 几种安装方式3.1 通过 ArcGIS CDN 获取 AMD 模块3.2 通过 NPM 运行 ES 模块3.3 通过 CDN 获取 ES 模块3.4 本地构建 ES3.5 本地构建 AMD 3 VSCode下载与安装2.1 下…...
【STM32】FSMC接口的复用和非复用
问题背景 在阅读《零死角玩转STM32—F103指南者》,以及《STM32F10x-中文参考手册》关于FSMC一章节的时候,对于在控制NOR/SRAM的时候使用到的引脚,在提到NOR器件的时候提到了地址复用和非复用接口,一时间没明白是什么东西。 结论 非复用模式…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
