前言

最近项目使用 SpringBoot 3 + Spring 6 搭建,接口文档准备一如既往的使用 Swagger 自动生成,引入 springfox-boot-starter 依赖,配置好相关的 Swagger 配置,结果启动报错,修改配置后启动不报错了,但是访问 swagger-ui/index.html 页面报404。

问题分析

一顿分析过后发现,最新的 springfox 3.0.0 (最后一次维护在2020年)仅支持 Spring 5.x,要想使用 springfox,最简单快捷的方式就是降版本,将 SpringBoot 的版本号从 3.x 修改为 2.7.x(或更低版本)。

新项目就是想体验最新的 SpringBoot 版本功能,这样一搞,岂不是本末倒置了,因此我就尝试了各种方法,企图通过修改 Spring Bean 属性等方式适配 Swagger ,结果发现 Spring 里面的对象属性是一层嵌一层,牵一发而动全身,菜鸡的我只好放弃。

企图尝试:

  1. 注册 WebMvcConfigurer Bean,重写 configurePathMatch(PathMatchConfigurer configurer) 方法。
  2. 在 PathMatchConfigurer 对象中,修改 patternParser 属性为 null ,再修改 pathMatcher 属性不为空,使得 patternParser 为空并且 preferPathMatcher 等于 true。
  3. 从而会影响 WebMvcConfigurationSupport#initHandlerMapping 方法的条件判断,进而影响 AbstractHandlerMapping 的 patternParser 属性为空,而 pathMatcher 不为空。

image-20240304165058399

  1. AbstractHandlerMapping 的实例对象为 RequestMappingHandlerMapping ,其 afterPropertiesSet() 方法会根据 patternParser 属性作为条件判断,进而 RequestMappingInfo.BuilderConfiguration config 的 patternParser 属性为空并且 pathMatcher 属性不为空。

image-20240304165617376

  1. RequestMappingInfo.DefaultBuilder#build() 方法会调用 RequestMappingInfo.BuilderConfiguration#getPatternParserToUse() 方法,该方法会返回上一步配置的 patternParser 属性。从而影响 build() 方法的条件判断,实例化 PatternsRequestCondition 对象,最后在实例化 RequestMappingInfo 对象时,pathPatternsCondition 为空,而 patternsCondition 不为空。

image-20240304165953003

  1. 然后,springfox 的 WebMvcRequestHandler#getPatternsCondition() 方法会拿取 RequestMappingInfo 的 patternsCondition 属性,作为参数实例化 WebMvcPatternsRequestConditionWrapper 对象。

image-20240304170619406

  1. 最后,WebMvcPatternsRequestConditionWrapper#getPatterns() 方法在使用 PatternsRequestCondition 属性时就不会出现空指针情况,程序正常启动。

image-20240304170853408

问题突破

源码分析告一段落后,我就开始在网络遨游,寻找广大群众的力量,最终在 stackoverflow 上找到了解决方案,也是在此处第一次了解到 springdoc-openapi 。

先说解决方法的最核心东西,引入以下 maven 依赖。

1
2
3
4
5
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.x.x</version>
</dependency>

注意:如果是 Spring Boot 1.x 或 2.x 的项目,springdoc-openapi 需要使用 1.x 的版本。

stackoverflow 帖子的链接地址:How to run Swagger 3 on Spring Boot 3

既然知道了可以使用 springdoc-openapi 解决,那就直接进入官方查看怎么使用。官方地址:springdoc.org

映入眼帘的介绍,让人为之心动,支持 SpringBoot 3 和 Swagger-ui !!!

image-20240304171925138

使用SpringDoc Openapi

第一步,引入 maven 依赖。

第二步,同 Swagger 一样,编写配置类,主要是 GroupedOpenApiOpenAPI 两个类,详见 从 SpringFox 迁移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("springshop-public")
.pathsToMatch("/public/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("springshop-admin")
.pathsToMatch("/admin/**")
.addOpenApiMethodFilter(method -> method.isAnnotationPresent(Admin.class))
.build();
}

@Bean
public OpenAPI springShopOpenAPI() {
return new OpenAPI()
.info(new Info().title("SpringShop API")
.description("Spring shop sample application")
.version("v0.0.1")
.license(new License().name("Apache 2.0").url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("SpringShop Wiki Documentation")
.url("https://springshop.wiki.github.org/docs"));
}

如果项目仅有一个 GroupedOpenApi 配置类,那么可以直接配置在 application.properties 下,例如:

1
2
springdoc.packagesToScan=package1, package2
springdoc.pathsToMatch=/v1, /api/balance/**

第三步,对于 API Interface 和 API Model ,springdoc-openapi 与 swagger-annotation 略有不同,替换方案如下:

image-20240304173127941

其中 @Schema 注解等同于 Swagger 的 @ApiModel@ApiModelProperty 两个注解,不过在使用 @Schema 时需要注解,注解的 name 属性等同于 @ApiModel 的 value ,注解的 title 属性等同于 @ApiModelProperty 的 value 。

至此,springdoc-openapi 就配置完毕了,启动项目,访问 http://server:port/context-path/swagger-ui.html 。

springdoc-openapi 支持自定义访问路径,修改属性配置如下:

1
2
# swagger-ui custom path
springdoc.swagger-ui.path=/doc.html

修改后,访问 http://server:port/context-path/doc.html 也是一样的效果,它都会转发为最终的地址 http://server:port/context-path/swagger-ui/index.html 。