你以为“写接口文档”很麻烦?那为什么别人能让它自动生成还带权限分组?
🏆本文收录于《滚雪球学SpringBoot 3》:
https://blog.csdn.net/weixin_43970743/category_12795608.html,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。
本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】https://blog.csdn.net/weixin_43970743/article/details/151115907,你想学习的都被收集在内,快速投入学习!!两不误。
若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》:https://blog.csdn.net/weixin_43970743/category_11599389.html,涵盖SpringBoot所有版本教学文章。
演示环境说明:
- 开发工具:IDEA 2021.3
- JDK版本: JDK 17(推荐使用 JDK 17 或更高版本,因为 Spring Boot 3.x 系列要求 Java 17,Spring Boot 3.5.4 基于 Spring Framework 6.x 和 Jakarta EE 9,它们都要求至少 JDK 17。)
- Spring Boot版本:3.5.4(于25年7月24日发布)
- Maven版本:3.8.2 (或更高)
- Gradle:(如果使用 Gradle 构建工具的话):推荐使用 Gradle 7.5 或更高版本,确保与 JDK 17 兼容。
- 操作系统:Windows 11
前言:别把 Swagger 当成“写文档工具”,它更像“接口契约打印机”
OpenAPI 的核心意思很朴素:用一种标准、语言无关的方式描述 HTTP API,让人和机器都能读懂。这不是玄学,是规范——OpenAPI Specification(OAS)就是这么定义自己的:语言无关、可让消费者在不看源码的情况下理解并交互接口。
而 SpringDoc 的价值更现实:它在运行时检查 Spring 项目(Controller、注解、参数、校验等),自动推断 API 语义并生成 OpenAPI 3 文档。
换句话说:你只要把“接口该是什么样”写在代码里(注解/模型/校验),它就帮你把契约打印出来,还顺手给你一个 Swagger UI 页面用来测试。
好,铺垫结束。下面进入正题:用 SpringDoc 替代 SpringFox,并把你给的大纲扩写成“能直接抄进项目”的版本(放心,我不会让你抄一堆没用的配置然后祈祷它能跑😅)。
1. 集成 SpringDoc:替代 SpringFox 的“正经姿势”
1.1 为什么不建议再用 SpringFox?
我不打算在这儿拉踩谁——但工程选型讲究一个“能持续维护”。SpringDoc 这条线的目标非常明确:紧跟 OpenAPI 3,并且对 Spring Boot 的适配更自然(尤其是 Spring Boot 3 / Spring Framework 6 / Jakarta 命名空间之后)。SpringDoc 官方也明确自己是为了自动生成 OpenAPI 3 文档而存在。
所以“替代 SpringFox”的动机,本质上不是“新潮”,而是:减少不可控的兼容成本。
1.2 依赖怎么加(Spring Boot 3 推荐)
下面以 Spring Boot 3.x + Spring Web MVC 为例(WebFlux 依赖名不同,但思路一样)。
Maven
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version><!-- 以你项目锁定版本为准 --></version>
</dependency>
SpringDoc 官方站点提供整体能力说明与配置入口。
关键访问路径(默认)
一般你会用到两类端点:
- OpenAPI JSON:
/v3/api-docs(可改路径) - Swagger UI:
/swagger-ui/index.html(可改路径)
友情吐槽一句:很多人遇到“能访问 /v3/api-docs,但 UI 404”,然后开始怀疑人生。别慌,八成是 Security/拦截器把静态资源挡了,后面我们会专门讲。
1.3 application.yml 常用配置(少而精)
SpringDoc 的配置项属于“标准 Spring 配置属性”,官方列得很全。
我这里挑最常用的:
springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html # 会重定向到 /swagger-ui/index.html
小建议:生产环境要不要开 UI?看你们团队规范。至少别默认对公网裸奔(你不尴尬,安全同事会帮你尴尬🙂)。
2. @Tag、@Operation、@Schema:把“自动生成”从能用变成好用
SpringDoc 会自动扫 Controller 和模型生成文档,但“自动”通常只保证“有”,不保证“清晰”。想让接口文档看起来像个正经产品(而不是像一坨接口列表),你得靠注解把信息补齐。
接下来我们用一个很典型的业务:用户登录 + 订单查询 做例子。
2.1 @Tag:给接口分组起名字(不然全堆一起很灾难)
Swagger-Core 的 @Tag 注解可以用在类或方法上,用来给单个 operation 或整个类的 operations 打标签。
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
@Tag(name = "User", description = "用户相关接口:注册、登录、个人信息")
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping("/login")
public String login() {
return "ok";
}
}
我的真实感受:
- 没有 @Tag 的文档像“超市仓库”——东西都有,但你找不到。
- 有了 @Tag 至少像“货架”——分类清楚,骂人都更有理有据。
2.2 @Operation:给每个接口补上“人类语言的说明书”
@Operation 用于定义一个 OpenAPI Operation,或者补充 operation 的额外属性(summary、description、responses 等)。
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.web.bind.annotation.*;
@Operation(
summary = "用户登录",
description = "使用邮箱+密码登录,成功后返回 accessToken。注意:密码不会在任何响应中回传(别想了😄)"
)
@PostMapping("/login")
public LoginResponse login(
@Parameter(description = "登录请求体") @RequestBody LoginRequest req
) {
// ...省略鉴权逻辑
return new LoginResponse("token");
}
实战建议(有点“老工程师碎碎念”):
summary写短一点:列表里一眼扫过去要能懂description写清楚边界:成功/失败、幂等性、是否缓存、是否需要权限- 重要字段别靠口口相传:写在 description 里,未来能救命
2.3 @Schema:模型字段不写清楚,前端同学就会用“意念”对接
@Schema 用于定义 OpenAPI 规范中的 Schema(可以标在类、字段、参数、方法等),并补充描述、示例、是否必填等信息。
2.3.1 请求/响应模型示例
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(name = "LoginRequest", description = "登录请求体")
public class LoginRequest {
@Schema(description = "邮箱", example = "dev@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
private String email;
@Schema(description = "密码(明文传输需走 HTTPS)", example = "P@ssw0rd!", requiredMode = Schema.RequiredMode.REQUIRED)
private String password;
// getter/setter ...
}
@Schema(name = "LoginResponse", description = "登录响应体")
public class LoginResponse {
@Schema(description = "访问令牌(JWT)", example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
private String accessToken;
public LoginResponse(String accessToken) { this.accessToken = accessToken; }
// getter/setter ...
}
2.3.2 常见“坑点”与写法建议
- example 别瞎写:写一个真实形态的值,Swagger UI 里测试会更顺滑
- requiredMode 建议明确:否则团队里会出现“你以为必填、我以为选填”的经典误会
- 对枚举字段最好写
description说明含义(比如 0/1/2 到底代表啥)
我见过最离谱的一次:状态字段叫
status,值是 1/2/3/9。没人写注释。
三个月后连作者本人都说:“9 是啥来着?”
——所以别省这点字,真的。
3. 配合 Spring Security:Swagger UI 到底该放行还是该加锁?
这个问题通常没有“唯一正确答案”,只有“适合你们团队的答案”。但不管你选哪条路,Spring Security 的授权配置一定得搞明白:哪些路径需要 permitAll,哪些需要 authenticated/hasRole。
Spring Security 官方文档里对 Servlet 环境下的请求授权(authorizeHttpRequests 等)讲得很清楚。
下面我给你两套常用策略:开发环境全放行 vs 生产环境受控访问。
3.1 策略 A:开发环境放行 Swagger(最常见)
适用场景:内网开发、联调频繁、UI 仅在 dev/test 开。
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
// 放行 OpenAPI 文档与 Swagger UI 静态资源
.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
// 其余接口走认证
.anyRequest().authenticated()
);
return http.build();
}
为什么要放行这么多?
因为 Swagger UI 本质是一堆静态资源 + 它会请求 OpenAPI JSON(/v3/api-docs)。你只放行一个路径,另外的资源还是会被拦,页面照样“白给”。(这就是前面我说的“能访问 JSON 但 UI 404”的高发原因。)
3.2 策略 B:生产环境只允许特定角色访问 Swagger
适用场景:生产可查文档,但只给运维/后端/支持人员。
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
// 文档与 UI 只允许 ADMIN 或 DOCS 角色
.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html")
.hasAnyRole("ADMIN", "DOCS")
.anyRequest().authenticated()
)
.httpBasic(basic -> {}); // 你也可以换成 JWT/OAuth2 等方式
return http.build();
}
小提醒:
- 如果你用的是 JWT 方案,也可以给 Swagger UI 配一个“登录入口”或在网关层做访问控制。
- 别把文档端点对公网完全开放(除非你们是公开 API 且有明确的安全策略)。
- Spring Security 6 的授权写法建议以官方 reference 为准(authorizeHttpRequests/requestMatchers 等)。
3.3 进阶:只在特定 Profile 启用 Swagger(更稳)
“我不想在生产暴露 UI,但测试环境要用”——这需求太常见了,常见到我都不想吐槽🙂。
做法:给配置类加 @Profile("dev") 或者用配置开关控制 springdoc.swagger-ui.enabled。
SpringDoc 官方属性里明确支持通过 springdoc.api-docs.enabled 来禁用端点。
# application-prod.yml
springdoc:
api-docs:
enabled: false
swagger-ui:
enabled: false
4. 多分组文档:Mobile API vs Web API(终于到“看起来很高级”的部分了)
当你的系统开始同时服务移动端和 Web 端,接口就会出现两种典型分裂:
- 同一个资源,但 Mobile 只要“轻量字段”,Web 要“全量字段”
- Mobile 走
/api/m/**,Web 走/api/web/** - 或者按包分:
controller.mobilevscontroller.web
这时候如果你还把所有接口堆在一个文档里……嗯,怎么说呢,体验大概等于:在 400 页 PDF 里找你想要的 2 行字📄。
SpringDoc 支持基于路径、包扫描等方式对 OpenAPI 文档分组;官方也提供了属性配置入口。
而 GroupedOpenApi.Builder 的能力(pathsToMatch/packagesToScan/pathsToExclude 等)在其 Javadoc 里也列得很清楚。
下面给两种方式:代码式分组(更灵活)、配置式分组(更省事)。
4.1 方式一:代码式分组(推荐,适合复杂筛选)
4.1.1 Mobile / Web 基于路径分组
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiGroupConfig {
@Bean
public GroupedOpenApi mobileApi() {
return GroupedOpenApi.builder()
.group("Mobile API")
.pathsToMatch("/api/m/**")
.build();
}
@Bean
public GroupedOpenApi webApi() {
return GroupedOpenApi.builder()
.group("Web API")
.pathsToMatch("/api/web/**")
.build();
}
}
这里的
pathsToMatch是 GroupedOpenApi builder 的核心能力之一。
4.1.2 基于包扫描分组(更适合团队协作)
@Bean
public GroupedOpenApi mobileApi() {
return GroupedOpenApi.builder()
.group("Mobile API")
.packagesToScan("com.example.demo.controller.mobile")
.build();
}
@Bean
public GroupedOpenApi webApi() {
return GroupedOpenApi.builder()
.group("Web API")
.packagesToScan("com.example.demo.controller.web")
.build();
}
同理,packagesToScan 也是 builder 提供的能力。
4.2 方式二:配置式分组(简单项目更省心)
SpringDoc 的 properties 文档里列出了可用配置项,并明确它依赖标准 Spring 配置文件。
典型写法(示意):
springdoc:
group-configs:
- group: Mobile API
paths-to-match: /api/m/**
- group: Web API
paths-to-match: /api/web/**
什么时候用配置式?
- 分组规则非常简单:按路径/包分就行
- 你不想写一堆 @Bean
什么时候用代码式?- 你需要更复杂的过滤:比如“只展示标注了 @Operation 的接口”、或者按 header/produces/consumes 分组(builder 支持这些维度)。
5. 把四个点串起来:一个“能跑、能看、能控权限、能分组”的最小完整示例
下面给你一个相对完整的骨架(你可以直接复制到项目里改包名):
5.1 Controller 示例(Mobile & Web)
@Tag(name = "Order", description = "订单接口")
@RestController
@RequestMapping("/api/m/orders")
public class MobileOrderController {
@Operation(summary = "移动端订单列表", description = "返回轻量字段:id、状态、总价")
@GetMapping
public List<MobileOrderDto> list() { return List.of(); }
}
@Tag(name = "Order", description = "订单接口")
@RestController
@RequestMapping("/api/web/orders")
public class WebOrderController {
@Operation(summary = "Web端订单列表", description = "返回全量字段:包含用户、明细、优惠信息")
@GetMapping
public List<WebOrderDto> list() { return List.of(); }
}
@Schema(description = "移动端订单 DTO(轻量)")
class MobileOrderDto {
@Schema(description = "订单ID", example = "10001")
public Long id;
@Schema(description = "订单状态", example = "PAID")
public String status;
@Schema(description = "总价(单位:分)", example = "19900")
public Integer totalFee;
}
@Schema(description = "Web端订单 DTO(全量)")
class WebOrderDto extends MobileOrderDto {
@Schema(description = "用户邮箱", example = "dev@example.com")
public String userEmail;
@Schema(description = "优惠金额(分)", example = "1000")
public Integer discountFee;
}
- @Tag:类/方法级标签
- @Operation:接口说明
- @Schema:模型说明
5.2 分组配置(Mobile vs Web)
@Configuration
public class OpenApiGroupConfig {
@Bean
public GroupedOpenApi mobileApi() {
return GroupedOpenApi.builder()
.group("Mobile API")
.pathsToMatch("/api/m/**")
.build();
}
@Bean
public GroupedOpenApi webApi() {
return GroupedOpenApi.builder()
.group("Web API")
.pathsToMatch("/api/web/**")
.build();
}
}
GroupedOpenApi builder 的 pathsToMatch 等能力可参考其 Javadoc。
5.3 Spring Security 放行/管控 Swagger
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
授权配置写法与机制参考 Spring Security Reference。
6. 一些“写了就少掉头发”的实战建议(不写你也会踩)
6.1 文档内容“过度自动化”的副作用:信息不对称
自动生成会让人误以为“文档一定正确”。但实际上:
- Controller 参数没写校验/没写描述 → 文档就含糊
- DTO 字段含义没写 → 使用方靠猜
- 接口行为(幂等、分页、排序规则)没写 → 联调靠吵
所以我的原则是:
能自动的交给 SpringDoc;不能自动的(语义、边界、约束)必须由人补齐。
这也是 springdoc FAQ 强调的方向:让文档尽可能贴近代码、尽量少改动,但允许用注解补充。
6.2 Swagger UI 是“测试入口”,别把它当成“生产调试神器”
UI 在 dev/test 很香,但在 prod:
- 权限要控
- 日志要留
- 最好配合网关/内网/堡垒机
别最后变成“谁都能点两下试试”的公开按钮。你以为是方便,安全同事看了想把你电脑电源拔了😅。
6.3 多分组不是为了炫技,是为了减少沟通成本
Mobile 和 Web 的接口混在一起,最常见的后果是:
- 前端同学点错接口调试
- 测试同学写错用例
- 新人接手以为“为什么同名接口返回字段不一样”
分组这件事一次做对,后面每天都省时间。
结语:你真正想要的不是“文档页面”,而是“接口契约能长期可信”
我见过太多“文档工具上了、页面也能打开”,但团队仍然天天扯皮的项目。问题不在工具,而在:接口契约没有被当成契约。
SpringDoc 能把“生成”这件事变得很省力;而 @Tag/@Operation/@Schema 能把“可读性”拉起来;Spring Security 决定它是“开发利器”还是“生产隐患”;多分组则决定团队协作效率是“丝滑”还是“互相瞪眼”。
把这四件事串起来,你的文档就不再是“摆设”,而是工程的一部分——很朴素,但很顶用。
🧧福利赠与你🧧
无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G PDF编程电子书、简历模板、技术文章Markdown文档等海量资料。
ps:本文涉及所有源代码,均已上传至Gitee:
https://gitee.com/bugjun01/SpringBoot-demo开源,供同学们一对一参考 Gitee传送门https://gitee.com/bugjun01/SpringBoot-demo,同时,原创开源不易,欢迎给个star🌟,想体验下被🌟的感jio,非常感谢❗
🫵 Who am I?
我是 bug菌:
- 热活跃于 CSDN:
https://blog.csdn.net/weixin_43970743| 掘金:https://juejin.cn/user/695333581765240| InfoQ:https://www.infoq.cn/profile/4F581734D60B28/publish| 51CTO:https://blog.51cto.com/u_15700751| 华为云:https://bbs.huaweicloud.com/community/usersnew/id_1582617489455371| 阿里云:https://developer.aliyun.com/profile/uolxikq5k3gke| 腾讯云:https://cloud.tencent.com/developer/user/10216480/articles等技术社区; - CSDN 博客之星 Top30、华为云多年度十佳博主&卓越贡献奖、掘金多年度人气作者 Top40;
- 掘金、InfoQ、51CTO 等平台签约及优质作者;
- 全网粉丝累计 30w+。
更多高质量技术内容及成长资料,可查看这个合集入口 👉 点击查看:https://bbs.csdn.net/topics/612438251 👈️
硬核技术公众号 「猿圈奇妙屋」https://bbs.csdn.net/topics/612438251 期待你的加入,一起进阶、一起打怪升级。
- End -
- 点赞
- 收藏
- 关注作者
评论(0)