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完整的集成方案已上架,欢迎查收:彩蛋~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/98704.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【校招VIP】前端算法考点之大数据相关

考点介绍&#xff1a; 大数据的关键技术分为分析技术和处理技术&#xff0c;可用于大数据分析的关键技术主要包括A/B测试&#xff0c;关联规则挖掘&#xff0c;数据挖掘&#xff0c;集成学习&#xff0c;遗传算法&#xff0c;机器学习&#xff0c;自然语言处理&#xff0c;模式…

彻底学会Unity从网上加载资源到场景

使用类WWW 该类实例化的对象可以存储多种多媒体资源&#xff0c;只需要在构造函数中附上可访问的资源链接 Unity 中&#xff0c;WWW 类用于实例化互联网上的资源&#xff0c;如文本、图像、音频和视频等。WWW 实例化的对象可以存储多种多媒体素材。以下是一些常见的例子&…

C#,《小白学程序》第十一课:双向链表(Linked-List)其二,链表的插入与删除的方法(函数)与代码

1 文本格式 /// <summary> /// 改进的车站信息类 class /// 增加了 链表 需要的两个属性 Last Next /// </summary> public class StationAdvanced { /// <summary> /// 编号 /// </summary> public int Id { get; set; } 0; ///…

Netty—FuturePromise

Netty—Future&Promise 一、JDK原生 Future二、Netty包下的 Future三、Promise1、使用Promise同步获取结果2、使用Promise异步获取结果.3、使用Promise同步获取异常 - sync & get4、使用Promise同步获取异常 - await5、使用Promise异步获取异常 在异步处理时&#xff0…

公信力不是儿戏:政府与非营利组织如何利用爱校对提升信息质量

公信力是政府和非营利组织成功的基础&#xff0c;而这种公信力大多来源于对外发布的信息的准确性和可靠性。在这个方面&#xff0c;“爱校对”展现了它的强大能力和实用性。以下我们将具体探讨这两种组织如何通过使用爱校对来提升他们的信息质量。 政府&#xff1a;公开与透明&…

基于Spring Boot的企业门户网站设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的企业门户网站设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java springboot…

【TypeScript学习】—面向对象(四)

【TypeScript学习】—面向对象&#xff08;四&#xff09; 一、面向对象 二、类 三、构造方法 class Dog{name:string;age:number;//构造函数constructor(name:string,age:number){this.namename;this.ageage;}bark(){//在方法中可以通过this来表示当前调用方法的对象//this表…

使用命令行创建仓库

如果你还没有任何代码&#xff0c;可以通过命令行工具创建一个全新的Git仓库并初始化到本项目仓库中。 git clone https://e.coding.net/***/neurosens.git cd neurosens echo "# neurosens" >> README.md git add README.md git commit -m "first commi…

简单的爬虫代码 爬(豆瓣电影)

路漫漫其修远兮&#xff0c;吾将上下而求索 这次写一个最简单的python爬虫代码&#xff0c;也是大多教程第一次爬取的&#xff0c;代码里面有个别的简单介绍&#xff0c;希望能加深您对python爬虫的理解。 本次爬取两个网页数据 一 爬取的网站 豆瓣电影 爬取网页中的&#…

windows查看端口占用,通过端口找进程号(查找进程号),通过进程号定位应用名(查找应用)(netstat、tasklist)

文章目录 通过端口号查看进程号netstat通过进程号定位应用程序tasklist 通过端口号查看进程号netstat 在Windows系统中&#xff0c;可以使用 netstat 命令来查看端口的占用情况。以下是具体的步骤&#xff1a; 打开命令提示符&#xff08;CMD&#xff09;&#xff1a;按WinR组…

鲁棒优化入门(6)—Matlab+Yalmip两阶段鲁棒优化通用编程指南(上)

0.引言 上一篇博客介绍了使用Yalmip工具箱求解单阶段鲁棒优化的方法。这篇文章将和大家一起继续研究如何使用Yalmip工具箱求解两阶段鲁棒优化(默认看到这篇博客时已经有一定的基础了&#xff0c;如果没有可以看看我专栏里的其他文章)。关于两阶段鲁棒优化与列与约束生成算法的原…

Mybatis 动态SQL – 使用choose标签动态生成条件语句

之前我们介绍了if,where标签的使用&#xff1b;本篇我们需要在if,where标签的基础上介绍如何使用Mybatis提供的choose标签动态生成条件语句。 如果您对if,where标签动态生成条件语句不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; Mybat…