目录

一、前言

二、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.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 文档的最佳选择。

参考资料

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐