当前位置: 首页 > news >正文

Spring Boot 集成 Swagger3 (OpenAPI) 接口文档实战

目录

一、前言

二、Swagger2 vs Swagger3 主要变化

三、环境准备

四、快速集成步骤

4.1 创建 Spring Boot 项目

4.2 添加 Swagger3 依赖

4.3 配置文件

4.4 OpenAPI 配置类

五、核心注解使用

5.1 创建实体类

5.2 创建请求/响应包装类

5.3 创建 Controller 示例

5.4 注解说明

六、高级配置

6.1 接口分组

6.2 隐藏接口

6.3 全局响应码配置

七、测试验证

八、常见问题及解决

8.1 访问 Swagger UI 出现 404

8.2 接口文档中没有显示实体类字段描述

8.3 生产环境不想暴露接口文档

九、总结


一、前言

在前后端分离的开发模式下,接口文档的重要性不言而喻。传统的手写文档方式存在维护困难、更新不及时、与代码脱节等问题。Swagger 作为一款优秀的接口文档生成工具,可以自动根据代码生成接口文档,极大地提高了开发效率。

随着 Swagger 的演进,Swagger3(即 OpenAPI 3.0 规范)相较于 Swagger2 有了较大的变化。本文将详细介绍如何在 Spring Boot 项目中集成 Swagger3,并展示常用的配置和注解使用方式。

二、Swagger2 vs Swagger3 主要变化

在开始之前,我们先了解一下 Swagger3 相比 Swagger2 的主要变化:

对比项Swagger2Swagger3
依赖包springfox-swagger2+springfox-swagger-uispringdoc-openapi-starter-webmvc-ui
访问地址/swagger-ui.html/swagger-ui/index.html
注解包io.swagger.annotationsio.swagger.v3.oas.annotations
核心注解@Api@ApiOperation@Tag@Operation
配置方式需要@EnableSwagger2注解自动配置,无需额外注解

三、环境准备

本文使用的开发环境:

  • JDK 17+

  • Spring Boot 3.x

  • Maven 3.6+

注意:Spring Boot 3.x 最低要求 JDK 17,且不再支持 Swagger2 的 springfox,因此我们使用 springdoc-openapi 来实现 Swagger3 集成。

四、快速集成步骤

4.1 创建 Spring Boot 项目

首先创建一个 Spring Boot 项目,添加 Web 依赖。

4.2 添加 Swagger3 依赖

pom.xml中添加 springdoc-openapi 依赖:

<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.6.0</version> </dependency>

这个依赖包含了:

  • OpenAPI 3.0 规范的支持

  • Swagger UI 界面

  • 自动配置功能

4.3 配置文件

application.yml中添加 Swagger 相关配置:

spring: application: name: swagger3-demo # SpringDoc 配置 springdoc: # 接口文档路径,默认为 /v3/api-docs api-docs: path: /v3/api-docs enabled: true # Swagger UI 路径,默认为 /swagger-ui.html swagger-ui: path: /swagger-ui.html enabled: true # 展开策略:none(不展开)、list(展开标签)、full(全部展开) doc-expansion: none # 请求超时时间 urls-primary-name: default # 包扫描路径,多个用逗号分隔 packages-to-scan: com.example.controller # 路径匹配规则 paths-to-match: /**

4.4 OpenAPI 配置类

创建一个配置类,用于自定义文档信息:

package com.example.config; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class OpenApiConfig { @Bean public OpenAPI customOpenAPI() { return new OpenAPI() .info(new Info() .title("Spring Boot 3.x 集成 Swagger3 接口文档") .version("1.0.0") .description("这是一份 Swagger3 接口文档示例") .contact(new Contact() .name("作者") .email("author@example.com") .url("https://example.com")) .license(new License() .name("Apache 2.0") .url("https://www.apache.org/licenses/LICENSE-2.0.html"))); } }

至此,Swagger3 已经集成完成!启动项目后访问http://localhost:8080/swagger-ui/index.html即可看到接口文档界面。

五、核心注解使用

Swagger3 提供了一系列注解来丰富接口文档内容。下面通过一个完整的 Controller 示例来演示常用注解的使用。

5.1 创建实体类

package com.example.entity; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data @Schema(description = "用户实体") public class User { @Schema(description = "用户ID", example = "1") private Long id; @Schema(description = "用户名", example = "张三", required = true) private String username; @Schema(description = "邮箱", example = "zhangsan@example.com") private String email; @Schema(description = "年龄", example = "25") private Integer age; }

5.2 创建请求/响应包装类

package com.example.dto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data @Schema(description = "通用响应结果") public class Result<T> { @Schema(description = "状态码", example = "200") private Integer code; @Schema(description = "提示信息", example = "success") private String message; @Schema(description = "数据") private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("success"); result.setData(data); return result; } public static <T> Result<T> error(String message) { Result<T> result = new Result<>(); result.setCode(500); result.setMessage(message); return result; } }
package com.example.dto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data @Schema(description = "用户查询参数") public class UserQueryDTO { @Schema(description = "用户名(模糊查询)", example = "张") private String username; @Schema(description = "最小年龄", example = "18") private Integer minAge; @Schema(description = "最大年龄", example = "60") private Integer maxAge; @Schema(description = "当前页码", example = "1", defaultValue = "1") private Integer pageNum = 1; @Schema(description = "每页条数", example = "10", defaultValue = "10") private Integer pageSize = 10; }

5.3 创建 Controller 示例

package com.example.controller; import com.example.dto.Result; import com.example.dto.UserQueryDTO; import com.example.entity.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/api/users") @Tag(name = "用户管理", description = "用户相关的接口") public class UserController { /** * 模拟数据存储 */ private final List<User> userList = new ArrayList<>(); /** * 获取用户列表 */ @GetMapping @Operation(summary = "获取用户列表", description = "分页查询用户信息") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))), @ApiResponse(responseCode = "500", description = "服务器内部错误") }) public Result<List<User>> getUsers( @Parameter(description = "用户名(模糊查询)") @RequestParam(required = false) String username, @Parameter(description = "年龄") @RequestParam(required = false) Integer age) { // 模拟查询逻辑 List<User> result = userList.stream() .filter(u -> username == null || u.getUsername().contains(username)) .filter(u -> age == null || u.getAge().equals(age)) .toList(); return Result.success(result); } /** * 获取用户详情 */ @GetMapping("/{id}") @Operation(summary = "获取用户详情", description = "根据ID获取用户详细信息") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "查询成功"), @ApiResponse(responseCode = "404", description = "用户不存在") }) public Result<User> getUserById( @Parameter(description = "用户ID", required = true, example = "1") @PathVariable Long id) { User user = userList.stream() .filter(u -> u.getId().equals(id)) .findFirst() .orElse(null); if (user == null) { return Result.error("用户不存在"); } return Result.success(user); } /** * 创建用户 */ @PostMapping @Operation(summary = "创建用户", description = "新增用户信息") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "创建成功"), @ApiResponse(responseCode = "400", description = "参数校验失败") }) public Result<User> createUser( @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "用户信息", required = true, content = @Content(schema = @Schema(implementation = User.class)) ) @RequestBody User user) { user.setId(System.currentTimeMillis()); userList.add(user); return Result.success(user); } /** * 更新用户 */ @PutMapping("/{id}") @Operation(summary = "更新用户", description = "根据ID更新用户信息") public Result<User> updateUser( @Parameter(description = "用户ID", required = true) @PathVariable Long id, @RequestBody User user) { // 模拟更新逻辑 user.setId(id); return Result.success(user); } /** * 删除用户 */ @DeleteMapping("/{id}") @Operation(summary = "删除用户", description = "根据ID删除用户") @Parameters({ @Parameter(name = "id", description = "用户ID", required = true, in = ParameterIn.PATH), @Parameter(name = "force", description = "是否强制删除", in = ParameterIn.QUERY) }) public Result<Void> deleteUser( @PathVariable Long id, @RequestParam(defaultValue = "false") Boolean force) { // 模拟删除逻辑 return Result.success(null); } /** * 条件查询用户(使用对象接收参数) */ @PostMapping("/search") @Operation(summary = "条件查询用户", description = "根据多个条件组合查询用户") public Result<List<User>> searchUsers(@RequestBody UserQueryDTO queryDTO) { // 模拟查询逻辑 List<User> result = userList.stream() .filter(u -> queryDTO.getUsername() == null || u.getUsername().contains(queryDTO.getUsername())) .filter(u -> queryDTO.getMinAge() == null || u.getAge() >= queryDTO.getMinAge()) .filter(u -> queryDTO.getMaxAge() == null || u.getAge() <= queryDTO.getMaxAge()) .toList(); return Result.success(result); } }

5.4 注解说明

注解作用使用位置
@Tag对 API 进行分组描述Controller 类
@Operation描述一个 API 操作方法
@Parameter描述接口参数方法参数、方法
@Parameters多个参数描述的组合方法
@ApiResponse描述响应信息方法
@ApiResponses多个响应描述的组合方法
@Schema描述模型属性实体类、DTO
@Content描述请求/响应内容配合 @ApiResponse 使用

六、高级配置

6.1 接口分组

当项目接口较多时,可以通过分组来组织文档:

springdoc: group-config: groups: - name: 用户管理 paths-to-match: /api/users/** packages-to-scan: com.example.controller.user - name: 订单管理 paths-to-match: /api/orders/** packages-to-scan: com.example.controller.order

或者通过代码方式配置分组:

@Configuration public class OpenApiGroupConfig { @Bean public GroupedOpenApi userApi() { return GroupedOpenApi.builder() .group("用户管理") .pathsToMatch("/api/users/**") .packagesToScan("com.example.controller.user") .build(); } @Bean public GroupedOpenApi orderApi() { return GroupedOpenApi.builder() .group("订单管理") .pathsToMatch("/api/orders/**") .packagesToScan("com.example.controller.order") .build(); } }

6.2 隐藏接口

如果需要隐藏某些接口,可以使用@Hidden注解:

@Hidden // 此接口不会出现在文档中 @GetMapping("/internal") public String internalApi() { return "内部接口"; }

6.3 全局响应码配置

@Configuration public class OpenApiConfig { @Bean public OpenAPI customOpenAPI() { return new OpenAPI() .components(new Components() .addResponses("BadRequest", new ApiResponse() .description("请求参数错误")) .addResponses("Unauthorized", new ApiResponse() .description("未授权")) .addResponses("Forbidden", new ApiResponse() .description("无权限访问")) .addResponses("NotFound", new ApiResponse() .description("资源不存在")) .addResponses("InternalServerError", new ApiResponse() .description("服务器内部错误"))) .info(...); } }

七、测试验证

启动 Spring Boot 应用,访问以下地址:

  • Swagger UI 界面http://localhost:8080/swagger-ui/index.html

  • OpenAPI JSON 文档http://localhost:8080/v3/api-docs

在 Swagger UI 界面中,可以看到:

  1. 分组后的接口列表

  2. 每个接口的请求方式、路径、描述

  3. 请求参数说明

  4. 响应示例

  5. 在线测试功能

八、常见问题及解决

8.1 访问 Swagger UI 出现 404

原因:可能没有添加依赖,或者 Spring Boot 版本不兼容。

解决:确保添加了正确的依赖,Spring Boot 3.x 必须使用springdoc-openapi-starter-webmvc-ui

8.2 接口文档中没有显示实体类字段描述

原因:没有使用@Schema注解标注实体类字段。

解决:在实体类字段上添加@Schema(description = "字段描述")

8.3 生产环境不想暴露接口文档

解决方法:通过配置控制是否启用

springdoc: api-docs: enabled: false # 关闭 API 文档 swagger-ui: enabled: false # 关闭 Swagger UI

或者通过 profile 控制:

springdoc: enabled: false

九、总结

本文详细介绍了在 Spring Boot 3.x 中集成 Swagger3(OpenAPI 3.0)的完整流程,包括:

  1. Swagger2 与 Swagger3 的主要区别

  2. 依赖引入和基础配置

  3. 核心注解的使用方法

  4. 高级配置(分组、隐藏接口、全局响应码)

  5. 常见问题及解决方案

通过 Swagger3,我们可以轻松地为 Spring Boot 项目生成规范、清晰的接口文档,并且支持在线调试功能,大大提升了前后端协作的效率。SpringDoc 作为 Swagger3 在 Spring Boot 中的官方推荐实现,配置简单、功能强大,是构建 RESTful API 文档的最佳选择。

参考资料

  • SpringDoc 官方文档

  • OpenAPI 3.0 规范

  • Swagger 注解参考

http://www.jsqmd.com/news/548197/

相关文章:

  • Wan2.2-T2V-A5B创意验证利器:快速将你的想法变成视频
  • 新手避坑指南:用树莓派Pico RP2040的I2C驱动OLED屏(SSD1306)完整流程
  • Qwen3-TTS-12Hz-1.7B-VoiceDesign在播客制作中的应用:自动化内容生成
  • 别只盯着POST请求!分析黑客攻击流量时,90%的人会忽略的HTTP响应包
  • 终极Windows右键菜单管理指南:如何快速清理和自定义你的右键菜单
  • Dify实战技巧:如何让智能客服自动识别并展示图文内容?
  • VS2010 Debug模式下函数栈帧的完整生命周期解析(附内存布局图)
  • Python脚本自动化Abaqus仿真:5个高效交互技巧(附实战代码)
  • Nunchaku-FLUX.1-devWebUI API对接:Python requests调用/Postman测试模板
  • M2LOrder轻量级服务实战:微信小程序后端集成M2LOrder API情感分析
  • 不止于安装:将Helowin Oracle 11g Docker镜像改造为可持续使用的开发数据库
  • Qwen3-VL-WEBUI镜像快速上手:无需深度学习基础,也能玩转多模态AI
  • 开发提效神器:用快马AI生成高性能Go并发哈希表,告别重复造轮子
  • bWAPP靶场实战:从SQL注入到XSS的完整通关指南(附详细Payload)
  • PowerShell实战:用户文件夹改名后如何批量修复注册表路径(附完整脚本)
  • 【03】软考软件设计师——CPU与指令系统考点精讲与真题突破
  • 解密Android 12日志分级机制:从VERBOSE到ASSERT的完整使用手册
  • 低成本GPU算力方案:nanobot轻量OpenClaw在单卡3090上稳定部署教程
  • Ostrakon-VL-8B可部署方案:从单机WebUI到K8s集群化门店AI中台
  • 保姆级教程:Kohya训练器从安装到中文配置全流程(含CUDNN加速技巧)
  • 外地患者来京就医找陪诊?四招避开行业陷阱,正规机构这样选 - 品牌排行榜单
  • 为什么92%的FastAPI AI项目卡在流式响应?揭秘async generator阻塞根源与3种非阻塞调度模式
  • 告别公式复制烦恼!LaTeX2Word-Equation让跨平台公式处理效率提升10倍
  • 如何解决华硕ROG笔记本性能调校难题?GHelper轻量工具全解析
  • PX4飞控+MID360实战:如何正确关闭罗盘并理解FAST-LIO定位下的坐标系‘魔术’
  • 游戏开发实战:如何用Bezier曲线打造流畅的3D角色动画路径(Unity/C#示例)
  • HG-ha/MTools生产环境:SaaS公司集成MTools API实现客户自助式AI内容生成
  • 逆向新手也能懂:用Python脚本5分钟搞定‘长城杯’EasyRe逆向题
  • C++轻量级HTTP库cpp-httplib:从嵌入式设备到企业服务的全场景解决方案
  • 别再死记公式了!用OpenCV+Python搞定机器人3D视觉手眼标定(眼在手外)