Spring Boot 集成 Swagger3 (OpenAPI) 接口文档实战
目录
一、前言
在前后端分离的开发模式下,接口文档的重要性不言而喻。传统的手写文档方式存在维护困难、更新不及时、与代码脱节等问题。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.html -
OpenAPI 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 文档的最佳选择。
参考资料
更多推荐




所有评论(0)