记我的Springboot2.6.4从集成swagger到springdoc的坎坷路~

news2024/12/27 0:56:38

项目背景

主要依赖及jdk信息:

Springboot:2.6.4

Jdk: 1.8

最近新搭建了一套管理系统,前端部分没有公司的前端团队,自己在github上找了一个star较多使用相对也简单的框架。在这个管理系统搭建好上线之后,给组内的小伙伴们分享时,前端同事第一次提出,他们可以参与进来,前端部分由他们改造,我这边只需要提供接口就行。

这时,接口是现成的,但缺文档,于是,第一想到的,就是集成swagger,想着添加依赖、在一些controller上添加对应注解,就能满足需求,之后调整代码也不用再单独去维护文档。所以,就开始我这为期2天的集成路~~~

集成过程:

集成方式一:直接添加swagger依赖

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

增加swagger配置

import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

}

启动项目,正常情况下可访问:http://host:port/context-path/swagger-ui.html查看swagger界面

遇到的问题

然而,项目启动失败,报错信息如下:

2024-12-25 12:19:59.619 [main]  WARN AbstractApplicationContext.java:591 - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

WARN AbstractApplicationContext.java:591 - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

详细信息:

2024-12-25 13:29:04.634 [main]  ERROR SpringApplication.java:830 - Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181)
	at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
	at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
	at java.lang.Iterable.forEach(Iterable.java:75)
	at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)
	at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
	at com.ifeng.hm.novel.admin.NovelAdminApplication.main(NovelAdminApplication.java:19)
Caused by: java.lang.NullPointerException: null
	at springfox.documentation.spi.service.contexts.Orderings$8.compare(Orderings.java:112)
	at springfox.documentation.spi.service.contexts.Orderings$8.compare(Orderings.java:109)
	at com.google.common.collect.ComparatorOrdering.compare(ComparatorOrdering.java:40)
	at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
	at java.util.TimSort.sort(TimSort.java:220)
	at java.util.Arrays.sort(Arrays.java:1438)
	at com.google.common.collect.Ordering.sortedCopy(Ordering.java:854)
	at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:57)
	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper$2.apply(DocumentationPluginsBootstrapper.java:138)
	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper$2.apply(DocumentationPluginsBootstrapper.java:135)
	at com.google.common.collect.Iterators$6.transform(Iterators.java:829)
	at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:52)
	at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:52)
	at com.google.common.collect.Iterators$ConcatenatedIterator.hasNext(Iterators.java:1400)
	at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:275)
	at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:239)
	at com.google.common.collect.FluentIterable.toList(FluentIterable.java:631)
	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.defaultContextBuilder(DocumentationPluginsBootstrapper.java:111)
	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.buildContext(DocumentationPluginsBootstrapper.java:96)
	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:167)
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)
	... 14 common frames omitted

出现这个异常的原因:

在SpringBoot2.6之后,Spring MVC 处理程序映射匹配请求路径的默认策略已从 AntPathMatcher 更改为PathPatternParser。

解决方案

那既然定位到原因就好办了,针对问题解决问题,于是,借助网络资源,开始尝试~~~

网上解决方案一:

降低springboot版本,降到2.6以下,但我项目中还依赖了spring-cloud-alibaba,nacos,setinel等等,降版本比较麻烦,这个方案pass掉了,继续探索

网上解决方案二

在springboot的配置文件(yaml或properties)中添加如下配置(示例为yaml):

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

这里的matching-strategy可选值有:

原因也很简单,既然springboot2.6之后,映射配置请求路径的默认策略从AntPathMatcher更改为了PathPatternParser,那通过配置手动改回应该就可以。

漫长探索之路

然页,添加配置后,项目启动,又失败了!!!这!不!科!学!为什么大家都留意说亲测有效,我这么配置就不行呢?

没有办法,从自身找问题吧,遇事不决先debug看看~

先找到setMatchingStrategy方法看看配置有没有生效,是没读取到配置,还是就使用了默认配置?

(org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties)

(org.springframework.boot.context.properties.bind.JavaBeanBinder.BeanProperty)

通过这里看,正确读取了配置,并调用set方法成功设置属性值。

近一步验证,配置没有问题!

继续debug...

在执行springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper#start之前的日志:

从这里的日志信息来看,Swagger2Controller注入也没有问题

Ok!那接下来重点看springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper#start方法

找到一个plugin,for循环中调用:

scanDocumentation(buildContext(each));

 继续debug,到这里抛出了异常:

对这个array做sort操作时,出现了NPE!那么,继续进到这个array看为什么会是这样的(重点0-2这三个元素,actuator)

在执行Arrays.sort(array)时用到的comparator是springfox.documentation.spi.service.contexts.Orderings

所以,出现了NullPointerException~

初现柳暗花明

那么,先不进一步看为什么,先来做个尝试,把/actuator排除或者直接移除依赖可不可行?试试看~

            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-actuator</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-boot-actuator-autoconfigure</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>

项目成功启动,打开http://localhost:8080/swagger-ui.html

正常!

终于正常了!

但是,如果actuator强依赖,不能exclude掉呢?对的,加回来,继续尝试~~~

尝试一:通过swagger配置,只匹配某些路径

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.ant("/admin/**"))
                .build();
    }

    /**
     * 添加摘要信息
     */
    private ApiInfo apiInfo()
    {
        // 用ApiInfoBuilder进行定制
        return new ApiInfoBuilder()
                // 设置标题
                .title("标题:管理系统_接口文档")
                // 描述
                .description("描述:用于管理xxx信息,具体包括XXX,XXX模块...")
                // 作者信息
                .contact(new Contact("xx@abc.com", null, null))
                // 版本
                .version("版本号:1.0.0")
                .build();
    }
}

虽然,这里配置了只select带有ApiOperation注解的method以及路径/admin/**,但是,对前面那个异常无效,并不妨碍它失败!!!

究其原因:

这个AddtionalHealthEndpointPathsWebMvcHandlerMapping依然会把/actuator/**加入之前的arrays中,而在array的sort时会因为PatternsCondition为null从而出现NPE!

那么,接下来,对WebMvcRequestHandlerProvider进行改造。

方法一:

自定义BeanPostProcessor在postProcessAfterInitialization方法中对PatternParser进行过滤

@Bean
    public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor()
    {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof WebMvcRequestHandlerProvider) {
                    customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
                }
                return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
            }
        };
    }

    private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
        List<T> copy = mappings.stream()
                .filter(mapping -> mapping.getPatternParser() == null) //
                .collect(Collectors.toList());
        mappings.clear();
        mappings.addAll(copy);
    }

    private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
        try {
            Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
            field.setAccessible(true);
            return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

重点:customizeSpringfoxHandlerMappings方法

经过filter之后的array

NullPointerException不见了!

方法二:

hack一个WebMvcRequestHandlerProvider,处理handlerMapping属性

在src目录下创建package:

springfox.documentation.spring.web.plugins

复制WebMvcRequestHandlerProvider到这个自建的同名的包下

修改构造方法:

同样,启动成功~~~

到这里,springboot2集成swagger过程中遇到的这个问题算是解决了。但是这个过程太痛苦了,费时,又费力~~~~

集成方式二:使用springfox-boot-starter

添加依赖:

            <!-- Swagger3依赖 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-boot-starter</artifactId>
                <version>${swagger.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.swagger</groupId>
                        <artifactId>swagger-models</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>

与集成方式一相同,会遇到同样的问题,解决方式也相同,不再赘述!

但是它有个问题

2020年之后,不再维护了~,这让人很不安啊!!!

集成方式三:接入springdoc,放弃springfox

添加依赖

        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-ui</artifactId>
            <version>1.8.0</version>
        </dependency>

注:1.8.0是最新支持springboot2.x和1.x的版本

增加简单配置类:

@Configuration
public class OpenApiConfig {
}

启动项目:

正常启动,没有出现方式一、方式二中遇到的问题,喜大普奔~~~~

springdoc的继续探索
分组与接口过滤
    /**
     * 配置过滤规则
     * 若不配置该GroupedOpenApi, 默认扫描所有接口并生成文档
     * @return
     */
    @Bean
    public GroupedOpenApi bookApi() {
        return GroupedOpenApi.builder()
                // 接口过滤,据此增加接口扫描规则(扫描@Operation注解标注的接口)。想皮一下的话,亦可自定义注解
                .addOpenApiMethodFilter(method -> method.isAnnotationPresent(Operation.class))
                .group("book") // 分组名,可建多个不同分组,分别扫描不同位置接口
                .pathsToMatch("/admin/book/**")
                .build();
    }
    /**
     * 配置过滤规则
     * 若不配置该GroupedOpenApi, 默认扫描所有接口并生成文档
     * @return
     */
    @Bean
    public GroupedOpenApi bookCategoryApi() {
        return GroupedOpenApi.builder()
                // 接口过滤,据此增加接口扫描规则(扫描@Operation注解标注的接口)。想皮一下的话,亦可自定义注解
                .addOpenApiMethodFilter(method -> method.isAnnotationPresent(Operation.class))
                .group("bookCategory") // 分组名,可建多个不同分组,分别扫描不同位置接口
                .pathsToMatch("/admin/category/**")
                .build();
    }

效果:

常见配置

见官方文档:OpenAPI 3 Library for spring-boot

接口鉴权与认证

启动类增加@SecurityScheme注解

@SecurityScheme(name = "api_token", type = SecuritySchemeType.HTTP, scheme ="bearer", in = SecuritySchemeIn.HEADER)
public class BookAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(NovelAdminApplication.class, args);
    }
}

配置类中配置API安全策略:

    @Bean
    public OpenAPI demoOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                                .title("Novel/Book API")
                                .description("novel-admin book,book-category,book-list related api docs")
                                .version("v1.0.0")
                )
                .security(List.of(new SecurityRequirement().addList("api_token")))//注意这里的api_token与启动类中的配置相同
                ;
    }

如果某个接口不需要安全认证(如login,logout之类),则可以在接口方法中添加如下注解:

@RestController
@RequestMapping("/healthChk")
public class HealthChkController {

    //@SecurityRequirements() 里面需要一个String数组,里面列出需要使用的@SecurityScheme,例如我们这里的api_token。如果不写就说明不需要任何的安全模式,这里就是这种情况。
    @SecurityRequirements()
    @RequestMapping("/check")
    public String healthChk() {
        return "ok";
    }
}
@SecurityRequirements() 里面需要一个String数组,里面列出需要使用的@SecurityScheme,例如我们这里的api_token。如果不写就说明不需要任何的安全模式,这里就是这种情况。

其他更详细的使用,可以阅读springdoc官方文档学习~~~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NNDL 作业11 LSTM

习题6-4 推导LSTM网络中参数的梯度&#xff0c; 并分析其避免梯度消失的效果 先来推个实例&#xff1a; 看式子中间&#xff0c;上半部分并未有连乘项&#xff0c;而下半部分有到的连乘项&#xff0c;从这可以看出&#xff0c;LSTM能缓解梯度消失&#xff0c;梯度爆炸只是不易…

uniapp使用live-pusher实现模拟人脸识别效果

需求&#xff1a; 1、前端实现模拟用户人脸识别&#xff0c;识别成功后抓取视频流或认证的一张静态图给服务端。 2、服务端调用第三方活体认证接口&#xff0c;验证前端传递的人脸是否存在&#xff0c;把认证结果反馈给前端。 3、前端根据服务端返回的状态&#xff0c;显示在…

MySQL中Performance Schema库的详解(下)

昨天说了关于SQL语句相关的&#xff0c;今天来说说性能相关的&#xff0c;如果没有看过上篇请点传送门https://blog.csdn.net/2301_80479959/article/details/144693574?fromshareblogdetail&sharetypeblogdetail&sharerId144693574&sharereferPC&sharesource…

YOLO11全解析:从原理到实战,全流程体验下一代目标检测

前言 一、模型介绍 二、网络结构 1.主干网络&#xff08;Backbone&#xff09; 2.颈部网络&#xff08;Neck&#xff09; 3.头部网络&#xff08;Head&#xff09; 三、算法改进 1.增强的特征提取 2.优化的效率和速度 3.更高的准确性与更少的参数 4.环境适应性强 5.…

【Qt】了解和HelloWorld

目录 0.用户交互界面风格 Windows下GUI开发方案&#xff1f; 1.Qt简介 1.1 版本Qt5. 1.2搭建Qt开发环境 需要安装3个工具 安装过程 熟悉QtSDK重要工具 2.使用Qt Creator创建项目 2.1代码解释 2.2helloworld 1.图形化方式 2.代码方式 0.用户交互界面风格 1.TUI&…

原点安全再次入选信通院 2024 大数据“星河”案例

近日&#xff0c;中国信息通信研究院和中国通信标准化协会大数据技术标准推进委员会&#xff08;CCSA TC601&#xff09;共同组织开展的 2024 大数据“星河&#xff08;Galaxy&#xff09;”案例征集活动结果正式公布。由工银瑞信基金管理有限公司、北京原点数安科技有限公司联…

【MySQL初阶】--- 数据类型

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; MySQL &#x1f3e0; 数据类型分类 MySQL是一套整体的对外数据存取方案,既然要存取数据,而数据有不同的类型,因此MySQL也存在不同的数据类型,有不同的用…

使用VsCode编译调试Neo4j源码

文章目录 使用VsCode编译调试Neo4j源码1 简介2 步骤1 下载源码2 依赖3 构建Neo4j4 运行5 安装VsCode扩展6 **调试** 使用VsCode编译调试Neo4j源码 1 简介 Neo4j作为领先的图数据库&#xff0c;在存储、查询上都非常值得分析学习。通过调试、日志等方法跟踪代码工作流有助于理…

从零开始构建美颜SDK:直播美颜插件的开发实践指南

很多人好奇的一个问题&#xff0c;直播APP中主播们的美颜功能是如何实现的&#xff0c;今天&#xff0c;我们将以构建一款美颜SDK为主线&#xff0c;从技术架构、功能设计到开发实践&#xff0c;为读者提供一个全面的指导。 一、美颜SDK的核心技术 要构建一款优秀的美颜SDK&a…

计算机网络习题( 第3章 物理层 第4章 数据链路层 )

第3章 物理层 一、单选题 1、下列选项中&#xff0c;不属于物理层接口规范定义范畴的是&#xff08; &#xff09;。 A、 接口形状 B、 引脚功能 C、 传输媒体 D、 信号电平 正确答案&#xff1a; C 2、在物理层接口特性中&#xff0c;用于描述完成每种功能的事件发…

云手机群控能用来做什么?

随着云手机的发展&#xff0c;云手机群控技术逐渐从小众的游戏多开工具&#xff0c;发展为涵盖多个领域的智能操作平台。不论是手游搬砖、短视频运营&#xff0c;还是账号养成等场景&#xff0c;云手机群控都展现出了强大的应用潜力。本文将为大家详细解析云手机群控的应用场景…

深度学习实验十七 优化算法比较

目录 一、优化算法的实验设定 1.1 2D可视化实验&#xff08;被优化函数为&#xff09; 1.2 简单拟合实验 二、学习率调整 2.1 AdaGrad算法 2.2 RMSprop算法 三、梯度修正估计 3.1 动量法 3.2 Adam算法 四、被优化函数变为的2D可视化 五、不同优化器的3D可视化对比 …

汽车IVI中控开发入门及进阶(43):NanoVG

NanoVG:基于OpenGL的轻量级抗锯齿2D矢量绘图库 NanoVG是一个跨平台、基于OpenGL的矢量图形渲染库。它非常轻量级,用C语言实现,代码不到5000行,非常精简地实现了一套HTML5 Canvas API,做为一个实用而有趣的工具集,用来构建可伸缩的用户界面和可视化效果。NanoVG-Library为…

【生信圆桌x教程系列】如何安装 seurat V4版本R包

生物信息分析,上云更省心; 欢迎访问 www.tebteb.cc 了解 【生信云】 一.介绍 Seurat 是一个广泛使用的 R 包&#xff0c;专门用于单细胞基因表达数据的分析与可视化。它主要被生物信息学和生物统计学领域的研究者用来处理、分析和理解单细胞 RNA 测序&#xff08;scRNA-seq&am…

阿里云技术公开课直播预告:基于阿里云 Elasticsearch 构建 AI 搜索和可观测 Chatbot

在当今数据驱动的商业环境中&#xff0c;企业面临着前所未有的挑战与机遇。如何高效搜索、分析和观测数据&#xff0c;已成为企业成功的关键。Elasticsearch 企业版作为 Elastic Stack 的商业发行版&#xff0c;提供了一整套高效的搜索、分析和观测解决方案。 为此&#xff0c…

android 登录界面编写

1、登录页面实现内容 1.实现使用两个EditText输入框输入用户名和密码。 2.使用CheckBox控件记住密码功能。 3.登录时候&#xff0c;验证用户名和密码是否为空。 4.当前CheckBox控件记住密码勾上时&#xff0c;使用SharedPreferences存储用户名和密码。 5.登录时候使用Prog…

多目标应用(一):多目标麋鹿优化算法(MOEHO)求解10个工程应用,提供完整MATLAB代码

一、麋鹿优化算法 麋鹿优化算法&#xff08;Elephant Herding Optimization&#xff0c;EHO&#xff09;是2024年提出的一种启发式优化算法&#xff0c;该算法的灵感来源于麋鹿群的繁殖过程&#xff0c;包括发情期和产犊期。在发情期&#xff0c;麋鹿群根据公麋鹿之间的争斗分…

设计模式——装饰模式

文章目录 1.定义2. 结构组成3. 组合模式结构4. 示例代码5. 模式优势6. 应用场景 1.定义 装饰模式就像是给你的对象穿上不同的 “时尚服装”&#xff0c;在程序运行时&#xff0c;你可以随意地给对象搭配各种 “服装” 来增加新的功能&#xff0c;而且完全不用对对象本身的 “身…

python+reportlab创建PDF文件

目录 字体导入 画布写入 创建画布对象 写入文本内容 写入图片内容 新增页 画线 表格 保存 模板写入 创建模板对象 段落及样式 表格及样式 画框 图片 页眉页脚 添加图形 构建pdf文件 reportlab库支持创建包含文本、图像、图形和表格的复杂PDF文档。 安装&…

<数据集>芝麻作物和杂草识别数据集<目标检测>

数据集下载链接 &#xff1c;数据集&#xff1e;芝麻作物和杂草识别数据集&#xff1c;目标检测&#xff1e;https://download.csdn.net/download/qq_53332949/90181548数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1300张 标注数量(xml文件个数)&#xff1a;130…