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 的主要变化:
| 对比项 | Swagger2 | Swagger3 |
|---|---|---|
| 依赖包 | springfox-swagger2+springfox-swagger-ui | springdoc-openapi-starter-webmvc-ui |
| 访问地址 | /swagger-ui.html | /swagger-ui/index.html |
| 注解包 | io.swagger.annotations | io.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.htmlOpenAPI JSON 文档:
http://localhost:8080/v3/api-docs
在 Swagger UI 界面中,可以看到:
分组后的接口列表
每个接口的请求方式、路径、描述
请求参数说明
响应示例
在线测试功能
八、常见问题及解决
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)的完整流程,包括:
Swagger2 与 Swagger3 的主要区别
依赖引入和基础配置
核心注解的使用方法
高级配置(分组、隐藏接口、全局响应码)
常见问题及解决方案
通过 Swagger3,我们可以轻松地为 Spring Boot 项目生成规范、清晰的接口文档,并且支持在线调试功能,大大提升了前后端协作的效率。SpringDoc 作为 Swagger3 在 Spring Boot 中的官方推荐实现,配置简单、功能强大,是构建 RESTful API 文档的最佳选择。
参考资料
SpringDoc 官方文档
OpenAPI 3.0 规范
Swagger 注解参考
