尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Spring Boot 3.x接口文档实战:springdoc-openapi替代Springfox全解析

Spring Boot 3.x接口文档实战:springdoc-openapi替代Springfox全解析 Spring Boot 3.x刚出来那阵子几乎所有从2.x升上来的老团队都撞上过同一堵墙以前项目里那个开箱即用的Swagger UI页面升级后一访问就是空白页或者直接404。换了Springfox的新版本也不行因为Springfox的维护基本停在了Spring Boot 2.x时代面对Boot 3的Jakarta命名空间直接没了下文。我身边不少同事折腾一两天后统一换成了springdoc-openapi这套方案用下来确实稳。今天这篇就围绕在Spring Boot 3.x项目里引入springdoc-openapi这件事把内置的Swagger UI、webmvc-api整套东西讲透包括版本对照、依赖选择、配置写法以及实际排坑经验给正准备升级或者新开3.x项目的朋友一个可以直接抄走的作业。1. 为什么Spring Boot 3.x必须换掉旧Swagger方案引入背景与选型1.1 javax到jakarta一场命名空间的迁徙Spring Boot 3.0做了一件影响面非常大的事从javax.迁移到jakarta.。这不是换个包名这么简单整个Servlet、JPA、Validation等基础API全部换了命名空间。以前写javax.servlet.http.HttpServletRequest的地方3.x里全都得改写成jakarta.servlet.http.HttpServletRequest。这是一个上游规范的调整Spring官方没有兼容选项只能跟着走。问题就出在这里市面上几大Swagger方案都是基于javax命名空间构建的。Springfox的核心代码长期没有适配jakarta导致Spring Boot 3.x启动后Springfox的自动配置类在尝试处理接口文档时要么类加载报错要么直接静默失效。很多团队升级完Spring Boot 3.x打开接口文档页面看到白屏第一反应是配置写错了实际上根因就是底层库不兼容。这个迁移还牵扯到另一个隐蔽问题Spring Boot 3.x要求JDK 17及以上而JDK模块化对反射、代理等机制的约束更严格老一代文档工具经常依赖一些非公开API的反射操作在新JDK下也会触发InaccessibleObjectException。所以就算有人把包名硬替换成jakarta反射层那一堆坑还是绕不过去。与其修补老方案不如换一个从设计上就面向新生态的方案。1.2 springfox为何在3.x集体失灵Springfox维护者其实在Spring Boot 2.x时代就有点跟不上了。Springfox 3.0.0发布后只支持到Spring Boot 2.6左右后面Boot持续更新Springfox并没有跟进兼容。到了Spring Boot 2.7官方开始引入spring.mvc.pathmatch.matching-strategyant_path_matcher这种配置来兼容Springfox其实已经是在打补丁。等Boot 3.x出现Springfox已经彻底断档。社区里很长一段时间流传着一个说法Springfox项目实际上已经停止维护了主页停留在老版本Issues也没人回复。这不是说Springfox有多差而是它在Spring Boot 3.x这个节点上已经尽完了历史使命。如果你用的项目还在Boot 2.x继续用Springfox问题不大但只要升到3.x就不建议再跟它死磕。后来官方用户手册里有一个专门的说明Boot 3的Starter文档中不再包含Springfox相关内容而是把Springdoc列为推荐的第三方文档方案。这个转折其实挺典型的说明Spring官方自己也承认接口文档这块第三方方案的选型已经变了。1.3 springdoc-openapi的技术选型理由springdoc-openapi从1.x时代就开始支持Spring Boot 2.x而且它对OpenAPI 3规范的支持非常完整。真正让它脱颖而出的是2.x版本直接面向Spring Boot 3.x设计artifactId重置命名空间对齐做到了“开箱即用零配置出文档”。我选它的理由还包括几个非常实际的点。第一它内置了Swagger UI。引入一个springdoc-openapi-starter-webmvc-ui依赖后UI页面和OpenAPI JSON端点都会自动注册不需要再单独引swagger-ui、swagger-core。这对很多只想让接口文档重新跑起来的人来说是最省事的方式。第二它原生支持OpenAPI 3。这意味着可以充分利用Tag、Operation、Parameter这些注解表达接口含义而不是老旧的Api、ApiOperation跟现在Spring生态的习惯也更一致。第三它支持分组文档。一个中大型后端项目接口可能上百个全堆在一个文档页里根本没法看。springdoc可以按包、按注解、按请求路径做分组每个组单独一个文档页面这个在实际项目里几乎必用。第四它和Spring Security的整合很干净。要放行文档路径只需要对/v3/api-docs/**和/swagger-ui/**做permitAll这个配置非常透明排查问题很容易。2. 环境准备与依赖引入固定版本不用再猜2.1 版本对照Boot 3.x与springdoc的版本对应关系引入springdoc-openapi之前先搞清楚版本对应关系否则会出现各种奇怪的启动报错。springdoc-openapi的1.x系列对应Spring Boot 2.xartifact是springdoc-openapi-ui2.x系列对应Spring Boot 3.xartifact是springdoc-openapi-starter-webmvc-ui。我整理了一个简要的版本对照表供参考Spring Boot版本springdoc-openapi版本artifact关键字2.2及以下1.2.xspringdoc-openapi-ui2.3 - 2.71.6.x / 1.7.xspringdoc-openapi-ui3.0 - 3.12.0.x - 2.2.xspringdoc-openapi-starter-webmvc-ui3.2及以上2.3.x及以上springdoc-openapi-starter-webmvc-ui版本选择上我一般推荐直接选取2.x里较新的稳定release。因为springdoc迭代节奏比较快新版本会修复Jakarta兼容细节和Spring Boot新版本带来的配置变化。截至现在的实践2.6.x、2.7.x这类版本都相对成熟大家可以在Maven Central上挑一个最近的正式版。注意如果你的项目用的是Spring Boot 3.2别再用1.x版本的springdoc或者Springfox即使代码能跑起来文档页也会出现各种异常情况最终还是要换2.x。2.2 Maven坐标与依赖树解读Maven项目引入方式非常简单在pom.xml里加一个依赖dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version2.6.0/version /dependency这一个依赖加完后项目里会多出好几样东西springdoc自身的核心模块、swagger-core、swagger-models、springdoc-ui以及swagger-ui的静态资源。也就是说Swagger UI其实是被该starter间接带进来的所以标题里说的“内置Swagger UI”指的就是这个效果不需要再单独引org.webjars:swagger-ui之类的包。如果项目是基于WebFlux的响应式工程那需要用另一个starterspringdoc-openapi-starter-webflux-ui。两个starter的分工很清晰webmvc-api这个系列是给Spring MVC传统Servlet栈用的。项目标题里特意点了webmvc-api说明场景就是常规Spring Boot Web应用用webmvc这个starter就对了。讲一个我实际遇到的场景曾有个同事为了图省事直接复制了同事的webflux starter到webmvc项目里结果接口文档一直空白因为WebFlux starter注册的自动配置类和Spring MVC环境不匹配。虽然它不会强制报错但自动配置会失效文档模块静默不工作。排查了老半天最后换成webmvc-ui就一切正常。所以这个artifact后缀真不是随便选的。2.3 Gradle场景与模块取舍Gradle项目同样简单implementation org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0如果不想引入整个UI模块只想要OpenAPI JSON端点也可以只引入springdoc-openapi-starter-webmvc-api。这个模块会生成/v3/api-docs这样的OpenAPI规范JSON但不带Swagger UI页面。这种方案适合那些前端自己用OpenAPI JSON生成客户端代码、后端不需要UI的团队。不过说句实话日常开发阶段我还是建议直接把UI带上联调和排错效率会高很多UI页面可以直接看接口的参数结构、响应模型比肉眼盯JSON方便太多。3. 核心配置实现从零跑通Swagger UI3.1 最小的application.yml配置引入依赖之后其实什么都不配置就能启动文档功能。Spring Boot启动后访问/swagger-ui/index.html就能看到UI页面访问/v3/api-docs能拿到OpenAPI JSON。但实际项目中一般都会做一些定制比如设置扫描包路径、自定义UI路径、调整分组。下面给一个最常见的起步配置springdoc: api-docs: path: /v3/api-docs enabled: true swagger-ui: path: /swagger-ui.html operations-sorter: method tags-sorter: alpha display-request-duration: true packages-to-scan: com.example.demo这段配置干了这么几件事api-docs.path指定OpenAPI JSON端点的地址默认就是/v3/api-docs保持默认即可。swagger-ui.path指定Swagger UI的访问路径改成/swagger-ui.html符合老用户习惯。packages-to-scan限定扫描哪个包下的Controller。如果不写springdoc会自动扫描整个Spring上下文里所有Controller。项目小无所谓项目大了建议还是显式配一下能避免把一些内部系统的接口也暴露出来。operations-sorter让UI页面里的接口按HTTP方法排序GET在前、POST在后看起来整齐很多。这里涉及一个底层机制springdoc在应用启动后会在Spring MVC的RequestMappingHandlerMapping里读取所有已注册的接口元数据然后把它们整理成OpenAPI文档模型。所以它能扫描到的接口与Spring MVC注册的接口集合是一一对应的。有些接口如果用了自定义的HandlerMapping或者不是标准的Controllerspringdoc可能扫不到这点后面排查部分会展开。3.2 自定义文档信息与分组引入依赖后默认的文档信息比较简陋比如没有标题、没有版本、没有描述。可以在项目中写一个OpenAPI的Bean来定制Configuration public class OpenApiConfig { Bean public OpenAPI customOpenAPI() { return new OpenAPI() .info(new Info() .title(订单服务API) .version(v1.0.0) .description(订单、支付、售后相关接口文档) .contact(new Contact() .name(后端研发组) .email(devexample.com))) .addServersItem(new Server() .url(https://api.example.com) .description(生产环境)); } }这里的info对应OpenAPI规范里的info节点UI页面左上角会展示。addServersItem则是声明请求的base地址如果不写Swagger UI默认用当前页面的地址作为server对本地联调来说没问题但部署到测试环境带了context-path之后往往会遇到调试地址不对的情况所以一开始就把Server配好比较稳妥。分组配置在项目稍微大一点之后几乎是刚需。比如订单模块和支付模块分开看可以这么配Bean public GroupedOpenApi orderGroup() { return GroupedOpenApi.builder() .group(订单模块) .packagesToScan(com.example.order) .pathsToMatch(/order/**) .build(); } Bean public GroupedOpenApi payGroup() { return GroupedOpenApi.builder() .group(支付模块) .packagesToScan(com.example.pay) .pathsToMatch(/pay/**) .build(); }配置了多个GroupedOpenApi之后Swagger UI页面右上角会出现一个分组下拉框可以在不同模块之间切换。这个功能非常实用尤其是接口数量到了大几十上百个全在一个页面滚动查找完全没法用。3.3 webmvc-api与Swagger UI的关系说明标题里有个“webmvc-api”很多初学者会疑惑这到底是什么东西。简单说它指的是springdoc为Spring MVC应用生成的那份OpenAPI规范描述API也就是/v3/api-docs这个JSON端点背后的实现模块。这份JSON里包含所有接口的请求方法、路径、参数、响应结构、数据模型等信息Swagger UI的作用则是把这个JSON渲染成好看的交互页面。两者是上下游关系不是并列关系。真正提供给前端或测试工具使用的是这份JSONSwagger UI只是它的可视化外壳。除了Swagger UI任何符合OpenAPI规范的客户端工具都能消费这份JSON比如Postman可以直接导入后端可以根据它生成TypeScript客户端测试平台可以拿它做自动化校验。理解了这层关系后遇到“UI能用但接口文档JSON访问不到”之类的问题就能大致判断方向是/v3/api-docs这条链路的配置出问题了。3.4 与Spring Security的权限放行配置实际项目里几乎都集成了Spring Security这种情况下文档路径默认是受保护的没登录就访问会跳转登录页。需要把文档相关路径放行Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth - auth .requestMatchers(/v3/api-docs/**, /swagger-ui/**, /swagger-ui.html).permitAll() .anyRequest().authenticated() ); return http.build(); } }这里要注意/v3/api-docs/**必须带上后面的/**因为实际请求会有/v3/api-docs、/v3/api-docs/swagger-config等子路径。只管放行/v3/api-docs一个地址的话UI页面能打开但页面里拉取JSON的请求可能还会被拦截控制台会看到401。我以前在一个内网系统里踩过这个坑UI页面上方一直转圈打开浏览器开发者工具发现/v3/api-docs返回401就是这个/**没写全。补上之后一切正常。也就是说权限放行不只是为了打开UI首页更要确保UI后续发起的那些异步请求都能通。4. 实操过程完整跑通一次4.1 新建项目与依赖引入我这里用一个最简单的Spring Boot 3.x项目来演示整套流程。创建工程这一步可以直接用Spring Initializr选择Java 17Spring Boot版本选3.3.x或者3.4.x。如果用的是Idea里的Spring Initializr注意在Dependencies里不用选任何额外组件后面我们在pom里自己加springdoc依赖。pom.xml核心内容parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.4/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version2.6.0/version /dependency /dependenciesspring-boot-starter-web是必须的因为springdoc的webmvc模块依赖Spring MVC环境。同时它会带来Tomcat、Jackson等组件Spring Boot 3.x自带的是Jackson 2.x和springdoc配合没有问题。至于热部署、测试包等按需加本次不做多余引入。注意不要用spring-boot-starter-webflux那是响应式Web环境。如果项目中同时存在webmvc和webflux依赖Spring Boot会优先自动配置WebFluxspringdoc的webmvc-ui模块可能不会被激活这又是一个很隐蔽的启动问题。4.2 一个简单的REST接口示例写一个标准的Controller确保接口能被扫描并展示在文档中RestController RequestMapping(/api/order) Tag(name 订单接口, description 订单相关操作) public class OrderController { Operation(summary 查询订单详情, description 根据订单ID查询订单信息) GetMapping(/{id}) public OrderDetail getOrder(Parameter(description 订单ID) PathVariable Long id) { return new OrderDetail(id, 示例订单, PAID); } Operation(summary 创建订单) PostMapping ResponseStatus(HttpStatus.CREATED) public OrderDetail createOrder(RequestBody Valid OrderCreateRequest request) { return new OrderDetail(1L, request.getProductName(), CREATED); } }再补一个数据模型类public record OrderDetail(Long id, String productName, String status) {}这里用Tag、Operation、Parameter这些注解表达接口信息。它们是io.swagger.v3.oas.annotations包下的注解对应OpenAPI 3规范。有些老项目还在用Api、ApiOperation那是Swagger 2时代的注解在springdoc下虽然老注解不会报错但效果已经不同步了建议迁移到OpenAPI 3注解。4.3 启动验证与页面观察启动应用后按顺序验证三个端点# 1. UI主页面 http://localhost:8080/swagger-ui/index.html # 2. OpenAPI规范JSON http://localhost:8080/v3/api-docs # 3. 带分组后的配置端点 http://localhost:8080/v3/api-docs/swagger-config打开Swagger UI页面后能看到订单接口这两个操作点击接口展开能看到请求参数、响应结构、数据模型定义。如果配置了OpenAPI的Bean页面左上角会显示标题“订单服务API”。如果页面空白先看控制台有没有异常或者直接访问/v3/api-docs测试JSON是否返回再双击刷新一下UI。一个快速判断技巧直接访问/v3/api-docs如果浏览器能返回一段结构化JSON说明服务端链路OK问题大概率出在UI静态资源或路径放行上面如果JSON都访问不到就说明springdoc自动配置没有生效或应用上下文不兼容优先检查starter类型和版本。5. 常见问题与排查技巧实录5.1 404与Whitelabel Error Page新手最常遇到的就是启动后访问/swagger-ui.html或/swagger-ui/index.html返回404白屏。这类问题多半不是springdoc没生效而是配置的路径和实际注册路径不匹配。默认情况下Swagger UI的首页地址是/swagger-ui/index.html配置了springdoc.swagger-ui.path/swagger-ui.html后/swagger-ui.html会做一个转发但真正的资源入口还是在/swagger-ui/**下。有些团队因此以为只配置path就够了结果直接访问带index.html时反而404。建议配置完路径后明确在文档或启动日志里记录UI地址别让团队里不同人用不同地址访问这个细节看着小维护时很影响效率。还有一类404是项目设置了server.servlet.context-path。比如context-path是/myapp那么UI地址就变成http://localhost:8080/myapp/swagger-ui/index.html。如果前端项目里配了代理注意将context-path一并带上。这个情况下OpenAPI JSON的地址也会跟着变成/myapp/v3/api-docs。5.2 接口扫描不到如果你的Controller已经写好了但Swagger UI里看不到对应接口先检查两件事。第一包扫描范围。默认情况下springdoc会扫描Spring Boot主类所在包及其子包。如果你的Controller在主包之外建议显式配置springdoc.packages-to-scan或者用Bean GroupedOpenApi来指定更精确的扫描范围。第二接口是不是用了非标准的路由注册方式比如WebMvcConfigurer里手动注册的HandlerMapping或者某些框架内部注册的接口。这些接口在RequestMappingHandlerMapping中能找到的多半没问题但如果接口是通过编程式路由建的springdoc不一定能识别需要额外处理。还有一个容易被忽略的点接口类上如果没有RestController或Controller注解只是简单地用RequestMapping标注在一个普通类上springdoc不会扫描到。解决办法是统一Controller注解规范这本身也是代码规范问题不只是文档问题。5.3 与Spring Security集成时404集成了Spring Security后出现404通常不是真的资源不存在而是请求被安全链路拦截后重定向了。因为UI页面会发起/v3/api-docs、/v3/api-docs/swagger-config等请求任何一个子路径没放行都会导致UI加载失败。我在实际项目里还遇到过一种情况security放行了/swagger-ui/**但没放行/v3/api-docs/**结果UI页面能打开但转圈加载不出来。这里建议直接执行请求确认一下/v3/api-docs有没有被重定向到登录页从而判断是否属于Security导致的请求拦截。如果存在网关或统一鉴权平台也要确认网关层有没有把这两个路径透传过去否则即使服务端放行了网关拦在前面也一样404。5.4 版本不匹配导致的启动异常如果启动日志里出现ClassNotFoundException: javax.servlet.*或者NoClassDefFoundError: jakarta.servlet.*十有八九是springdoc版本和Boot版本不匹配。比如在Boot 3.x里用1.x版本springdoc或者反过来在Boot 2.x里用2.x版本springdoc。解决办法很简单把springdoc升到2.x并确认artifact用的是springdoc-openapi-starter-webmvc-ui不是老的springdoc-openapi-ui。如果项目是从Boot 2.x升级上来的还需要把代码里Springfox时代的注解换掉否则即使文档能启动接口描述信息还是老式注解的注释表达无法与OpenAPI 3模型兼容。6. 避坑汇总与后续扩展方向6.1 项目状态与文档质量接口文档这东西配置好了不代表万事大吉。我对文档质量的判断标准很简单团队里任何一个人打开Swagger UI不需要翻源码就能知道每个接口的参数含义、必传字段和响应结构。要达到这个效果Operation描述必须写清楚DTO字段上该加的Schema注解也得加上不然UI页面里只能看到一堆名为field1的裸字段。springdoc有一个很方便的能力它会自动从Jackson和Bean Validation注解中提取字段约束信息。比如DTO里的NotBlank、Min、Max很多会体现在OpenAPI文档的schema节点中。所以写请求DTO的时候尽量把校验注解写完整不仅代码层面受益文档层面也会自动变清晰。我就见过有团队完全不写校验注解文档里所有字段都是“可选”联调时全靠口口相传效率非常低。6.2 我给新人的配置清单结合近期的项目实践我给出一份可以直接参考的“标准配置单”适用于大多数基于Spring Boot 3.x的Web API项目springdoc: api-docs: path: /v3/api-docs enabled: true swagger-ui: path: /swagger-ui.html display-request-duration: true operations-sorter: method tags-sorter: alpha packages-to-scan: com.example paths-to-match: /api/**这个配置有几个用意。paths-to-match可以只暴露/api/**下的接口把健康检查、内部处理接口排除在文档外。display-request-duration让UI页面显示每次请求耗时调试时比较有用。packages-to-scan则保证扫描范围可控。不同团队可以根据自己的接口前缀调整不一定非得是/api但建议至少设一个统一前缀这样文档和网关路由的对应关系也更清晰。如果项目同时需要多个环境展示可以在OpenAPIBean里声明不同环境的Server。多环境配置用Spring Profile也很方便。这类配置一旦沉淀成团队规范后续新项目直接复制粘贴能省掉很多重复沟通成本。6.3 结合代码生成与自动化测试的补充思路springdoc生成的这套OpenAPI JSON除了人工在浏览器里查看还有一个很大的价值是给自动化工具消费。如果你的团队在用Postman可以把/v3/api-docs导入Postman一键生成全部接口集合省去手工逐个录入的麻烦。更进一步OpenAPI JSON还能配合OpenAPI Generator之类的工具产出TypeScript客户端代码前端开发完全不用关心后端接口的路径和参数拼装。不过这里要提醒一点代码生成的前提是后端文档质量足够高字段命名、类型映射、枚举定义都得清晰。如果文档里一堆字段没有说明生成的客户端代码也只是把垃圾代码换个语言而已。所以这个方向虽然好但它不是“配完了再补文档”就能享用的必须把写文档当作写接口的一部分。6.4 我实际使用中的一点体会从Spring Boot 2.x升级到3.x的那个迁移期我把springfox从项目里彻底移除后重新用springdoc搭文档整个过程其实只花了一个下午。最费时间的不是配置本身而是把老代码里的Api、ApiModelProperty这些注解逐个替换成Tag、Operation、Schema。替换完以后文档展示和代码内联提示都舒服了很多之后在几个新项目里再也没折腾过接口文档相关的问题。springdoc这套方案最大的优点在于它紧跟Spring生态的节奏不需要你去理解一堆底层适配细节只要版本对得上、包路径对了它就能安安静静地把活干完。如果你正在或即将启动一个基于Spring Boot 3.x的后端项目我建议直接把springdoc-openapi当成默认依赖加进去它会成为你开发联调过程中一个非常省心的存在。
返回列表