最新版 springdoc-openapi-starter-webmvc-ui 常用注解详解 + 实战示例

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.6k人参与

当然可以!在 Spring Boot 3 + SpringDoc OpenAPI(Swagger 3 替代方案)生态中,springdoc-openapi-starter-webmvc-ui 是目前官方推荐的集成方式。它提供了一套丰富的注解,用于精细化控制 API 文档的生成,提升前端、测试、产品等协作方的体验。


最新版 springdoc-openapi-starter-webmvc-ui 常用注解详解 + 实战示例

📌 当前最新稳定版本:springdoc-openapi 2.5+(2025年仍适用)
📌 所有注解位于包:io.swagger.v3.oas.annotations.*


🧩 一、核心注解概览

注解作用适用位置
@OpenAPIDefinition全局 API 信息配置(标题、版本、联系人等)@Configuration
@Tag标记 Controller 或方法所属的“标签/模块”类、方法
@Operation描述某个 API 操作(方法)的详细信息方法
@Parameter描述单个请求参数(路径、查询、Header)方法参数
@Parameters批量描述多个参数方法
@RequestBody描述请求体结构方法参数
@ApiResponse描述单个响应状态码及结构方法
@ApiResponses批量描述多个响应方法
@Schema描述 DTO 字段的含义、示例、格式等类、字段、方法参数
@Hidden隐藏某个 Controller 或方法,不生成文档类、方法、字段

🧱 二、详细注解说明 + 实战示例(带中文注释)


1️⃣ @Tag —— 模块分组标签

用于对 Controller 或方法进行分组,前端文档左侧菜单按 Tag 分组显示。

@Tag(name = "用户管理模块", description = "提供用户增删改查、状态管理等核心功能")
@RestController
@RequestMapping("/api/users")
public class UserController {
    // ...
}

作用

  • 在 Swagger UI 左侧菜单中显示为“用户管理模块”。
  • 可用于模块化组织大型项目 API。

2️⃣ @Operation —— API 操作描述

描述某个接口的用途、注意事项、是否已废弃等。

@Operation(
    summary = "分页查询用户列表",
    description = "支持按用户名、年龄范围、状态筛选,返回分页数据。\n" +
                  "createTime 字段为 ISO 格式,如:2025-04-01T10:00:00",
    tags = {"用户管理模块"}, // 可覆盖类上的 Tag
    deprecated = false,      // 是否废弃
    security = @SecurityRequirement(name = "Bearer Token") // 安全要求(需配合 SecurityScheme)
)
@GetMapping
public Result<IPage<UserVO>> listUsers(
        @Parameter(description = "当前页码,从1开始", example = "1") @RequestParam(defaultValue = "1") Integer current,
        @Parameter(description = "每页大小,最大100", example = "10") @RequestParam(defaultValue = "10") Integer size,
        UserQueryDTO query) {
    // ...
}

作用

  • summary:接口简短标题(必填,显示在接口列表)
  • description:详细说明,支持 Markdown
  • deprecated:标记为废弃接口(UI 会显示删除线)
  • security:声明该接口需要认证(需全局配置 SecurityScheme)

3️⃣ @Parameter —— 单个参数描述

用于描述 @RequestParam, @PathVariable, @RequestHeader 等参数。

@GetMapping("/{id}")
@Operation(summary = "根据ID获取用户详情")
public Result<UserVO> getUserById(
        @Parameter(
            name = "id",
            description = "用户唯一标识,雪花ID",
            required = true,
            example = "123456789012345678",
            schema = @Schema(type = "integer", format = "int64")
        )
        @PathVariable Long id) {

    User user = userService.getById(id);
    if (user == null) {
        return Result.error(404, "用户不存在");
    }
    return Result.success(userStructMapper.toVO(user));
}

作用

  • name:参数名(通常可省略,自动推断)
  • description:参数说明
  • required:是否必填
  • example:示例值(非常重要!前端可一键填充)
  • schema:指定数据类型和格式(如 int64、date-time、email 等)

4️⃣ @RequestBody —— 请求体描述

用于描述 @RequestBody 注解的 DTO 对象结构。

@PostMapping
@Operation(summary = "创建新用户")
public Result<String> createUser(
        @io.swagger.v3.oas.annotations.parameters.RequestBody(
            description = "用户创建请求体",
            required = true,
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(implementation = UserCreateDTO.class),
                examples = {
                    @ExampleObject(
                        name = "创建普通用户",
                        value = """
                                {
                                  "name": "张三",
                                  "age": 25,
                                  "email": "zhangsan@example.com",
                                  "status": 1
                                }
                                """
                    ),
                    @ExampleObject(
                        name = "创建未成年用户",
                        value = """
                                {
                                  "name": "小明",
                                  "age": 16,
                                  "email": "xiaoming@example.com",
                                  "status": 0
                                }
                                """
                    )
                }
            )
        )
        @Valid @RequestBody UserCreateDTO createDTO) {
    boolean saved = userService.createUser(createDTO);
    return saved ? Result.success("用户创建成功") : Result.error("创建失败");
}

作用

  • description:请求体说明
  • content.schema.implementation:指定 DTO 类,自动生成字段文档
  • examples:提供多个示例(前端可切换使用)
  • 支持 JSON/YAML 示例

💡 注意:不要与 org.springframework.web.bind.annotation.RequestBody 混淆,这里是 io.swagger.v3.oas.annotations.parameters.RequestBody


5️⃣ @Schema —— 字段/类级描述(最常用!)

用于描述 DTO/Entity 的字段含义、格式、示例、是否只读等。

@Data
@Schema(description = "用户创建请求参数")
public class UserCreateDTO {

    @Schema(
        description = "用户名,2-20位中文或英文",
        example = "张三",
        minLength = 2,
        maxLength = 20,
        requiredMode = Schema.RequiredMode.REQUIRED // 必填
    )
    @NotBlank(message = "用户名不能为空")
    private String name;

    @Schema(
        description = "年龄,0-150岁",
        example = "25",
        minimum = "0",
        maximum = "150",
        requiredMode = Schema.RequiredMode.REQUIRED
    )
    @NotNull(message = "年龄不能为空")
    @Min(value = 0, message = "年龄不能小于0")
    private Integer age;

    @Schema(
        description = "邮箱地址",
        example = "zhangsan@example.com",
        format = "email", // 格式校验提示
        requiredMode = Schema.RequiredMode.NOT_REQUIRED
    )
    @Email(message = "邮箱格式不正确")
    private String email;

    @Schema(
        description = "用户状态:0=禁用,1=启用",
        example = "1",
        allowableValues = {"0", "1"}, // 枚举值提示
        requiredMode = Schema.RequiredMode.REQUIRED
    )
    @NotNull(message = "状态不能为空")
    private Integer status;
}

作用

  • 自动生成字段说明、示例、校验规则提示
  • format:支持 date, date-time, email, uuid, uri
  • allowableValues:枚举值提示(前端下拉可选)
  • accessMode:可设置 READ_ONLY(仅响应)或 WRITE_ONLY(仅请求,如密码字段)
  • implementation:用于嵌套对象或接口类型

6️⃣ @ApiResponse & @ApiResponses —— 响应结构描述

描述不同 HTTP 状态码对应的响应结构,特别是错误码。

@PutMapping
@Operation(summary = "更新用户信息")
@ApiResponses(value = {
    @ApiResponse(
        responseCode = "200",
        description = "更新成功",
        content = @Content(schema = @Schema(implementation = Result.class))
    ),
    @ApiResponse(
        responseCode = "400",
        description = "参数校验失败",
        content = @Content(schema = @Schema(implementation = Result.class))
    ),
    @ApiResponse(
        responseCode = "404",
        description = "用户不存在",
        content = @Content(schema = @Schema(implementation = Result.class))
    ),
    @ApiResponse(
        responseCode = "500",
        description = "系统内部错误",
        content = @Content(schema = @Schema(implementation = Result.class))
    )
})
public Result<String> updateUser(@Valid @RequestBody UserUpdateDTO updateDTO) {
    boolean updated = userService.updateUser(updateDTO);
    return updated ? Result.success("更新成功") : Result.error("更新失败");
}

作用

  • 前端可预知不同状态码的响应结构
  • 配合全局异常处理器,可标准化错误返回
  • responseCode:HTTP 状态码
  • description:状态码含义
  • content.schema:指定响应体结构(通常为 Result<T>

7️⃣ @Hidden —— 隐藏接口或字段

隐藏不想暴露给前端的接口或敏感字段。

@Hidden // 整个 Controller 不生成文档
@RestController
@RequestMapping("/api/internal")
public class InternalController {
    // ...
}

// 或隐藏某个字段(如密码、密钥)
@Data
public class UserVO {
    private Long id;
    private String name;

    @Hidden // 不在 Swagger 中显示
    private String internalCode;

    private String email;
}

作用

  • 隐藏内部接口(如运维、回调、定时任务触发接口)
  • 隐藏敏感字段(如密码、密钥、审计字段)

8️⃣ @OpenAPIDefinition —— 全局 API 信息配置

通常放在启动类或独立配置类中,定义 API 全局元信息。

@OpenAPIDefinition(
    info = @Info(
        title = "企业用户管理系统 API 文档",
        version = "v1.2.0",
        description = "提供用户管理、权限控制、操作日志等核心功能",
        contact = @Contact(
            name = "API支持团队",
            email = "api-support@company.com",
            url = "https://www.company.com"
        ),
        license = @License(
            name = "Apache 2.0",
            url = "https://www.apache.org/licenses/LICENSE-2.0.html"
        )
    ),
    servers = {
        @Server(url = "http://localhost:8080", description = "本地开发环境"),
        @Server(url = "https://api.company.com", description = "生产环境")
    }
)
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

作用

  • 定义 API 标题、版本、描述、联系人、许可证
  • 定义多环境服务器地址(前端可切换)
  • 显示在 Swagger UI 顶部

9️⃣ @SecurityScheme + @SecurityRequirement —— 安全认证配置

配置 JWT/Bearer Token 认证方式。

@SecurityScheme(
    name = "Bearer Token",
    type = SecuritySchemeType.HTTP,
    bearerFormat = "JWT",
    scheme = "bearer" // 小写 bearer
)
@Configuration
public class OpenApiConfig {
    // 可放在此类,或与 @OpenAPIDefinition 合并
}

然后在需要认证的接口上添加:

@Operation(
    summary = "删除用户",
    security = @SecurityRequirement(name = "Bearer Token") // 引用上面定义的 name
)
@DeleteMapping("/{id}")
public Result<String> deleteUser(@PathVariable Long id) {
    // ...
}

作用

  • Swagger UI 顶部会出现 🔒 认证按钮
  • 前端可输入 Token,后续请求自动带 Authorization: Bearer xxx
  • 提升 API 安全性和测试便利性

🧪 三、完整 Controller 示例(整合所有注解)

package com.example.demo.controller;

import com.example.demo.entity.dto.*;
import com.example.demo.entity.vo.UserVO;
import com.example.demo.service.IUserService;
import com.example.demo.util.Result;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
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.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

@Tag(name = "用户管理模块", description = "提供用户增删改查、状态管理、批量操作等核心功能")
@Validated
@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private IUserService userService;

    @Autowired
    private UserStructMapper userStructMapper;

    // ==================== 查询 ====================

    @Operation(
        summary = "分页查询用户列表",
        description = """
            支持多条件组合查询:
            - 按用户名模糊匹配
            - 按年龄范围筛选
            - 按状态精确匹配
            - 按创建时间区间筛选
            返回标准分页结构。
            """,
        security = @SecurityRequirement(name = "Bearer Token")
    )
    @Parameters({
        @Parameter(name = "current", description = "当前页码,从1开始", example = "1", required = true),
        @Parameter(name = "size", description = "每页大小,建议10~50", example = "10", required = true)
    })
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
        @ApiResponse(responseCode = "401", description = "未授权", content = @Content(schema = @Schema(implementation = Result.class)))
    })
    @GetMapping
    public Result<IPage<UserVO>> listUsers(
            @Min(1) @RequestParam(defaultValue = "1") Integer current,
            @Min(1) @RequestParam(defaultValue = "10") Integer size,
            UserQueryDTO query) {
        Page<User> page = new Page<>(current, size);
        IPage<User> userPage = userService.searchUsers(query, page);
        List<UserVO> voList = userStructMapper.toVOList(userPage.getRecords());
        IPage<UserVO> voPage = new Page<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
        voPage.setRecords(voList);
        return Result.success(voPage);
    }

    // ==================== 创建 ====================

    @Operation(
        summary = "创建新用户",
        description = "创建用户时会自动填充创建时间,状态默认启用"
    )
    @io.swagger.v3.oas.annotations.parameters.RequestBody(
        description = "用户创建请求体",
        required = true,
        content = @Content(
            mediaType = "application/json",
            schema = @Schema(implementation = UserCreateDTO.class),
            examples = {
                @ExampleObject(
                    name = "成人用户",
                    value = """
                            {
                              "name": "张三",
                              "age": 25,
                              "email": "zhangsan@example.com",
                              "status": 1
                            }
                            """
                ),
                @ExampleObject(
                    name = "未成年用户",
                    value = """
                            {
                              "name": "小明",
                              "age": 16,
                              "email": "xiaoming@example.com",
                              "status": 0
                            }
                            """
                )
            }
        )
    )
    @PostMapping
    public Result<String> createUser(@Valid @RequestBody UserCreateDTO createDTO) {
        boolean saved = userService.createUser(createDTO);
        return saved ? Result.success("用户创建成功") : Result.error("创建失败");
    }

    // ==================== 更新 ====================

    @Operation(summary = "更新用户信息")
    @PutMapping
    public Result<String> updateUser(@Valid @RequestBody UserUpdateDTO updateDTO) {
        boolean updated = userService.updateUser(updateDTO);
        return updated ? Result.success("更新成功") : Result.error("更新失败");
    }

    // ==================== 删除 ====================

    @Operation(
        summary = "删除用户(逻辑删除)",
        description = "不会物理删除数据,仅标记 deleted=1",
        security = @SecurityRequirement(name = "Bearer Token")
    )
    @Parameter(name = "id", description = "用户ID", required = true, example = "123456789012345678")
    @DeleteMapping("/{id}")
    public Result<String> deleteUser(@PathVariable @NotNull Long id) {
        boolean deleted = userService.removeById(id);
        return deleted ? Result.success("删除成功") : Result.error("用户不存在或已被删除");
    }
}

✅ 四、最佳实践建议

实践项说明
所有公开接口必须加 @Operation至少写 summary,让前端知道接口用途
DTO 字段必须加 @Schema描述 + 示例 + 校验规则,极大提升协作效率
关键参数加 @Parameter(example=...)前端可一键填充测试数据
提供多个 @ExampleObject覆盖正常、边界、异常场景
敏感接口加 @SecurityRequirement明确告知需要 Token
废弃接口加 deprecated = true前端可见,避免误用
内部接口用 @Hidden避免文档混乱
全局配置 @OpenAPIDefinition统一团队 API 文档风格

🌐 五、访问与调试

  • 文档地址:http://localhost:8080/swagger-ui/index.html
  • OpenAPI JSON:http://localhost:8080/v3/api-docs
  • 支持 Try it out → 在线调试 → 自动生成 Curl/Request

✅ 六、总结

通过合理使用 SpringDoc OpenAPI 注解,你可以:

✅ 生成专业级、可交互、可调试的 API 文档
减少前后端沟通成本,提升开发效率
标准化接口设计,避免“口口相传”
提升项目专业度和可维护性

💡 提示:注解虽好,但不要过度堆砌。保持简洁、实用、一致即可。大型项目建议制定《API 文档规范》,统一注解使用标准。


📌 官方文档参考:https://springdoc.org/


这份指南涵盖了企业开发中 95% 以上的 SpringDoc 注解使用场景,可直接用于生产项目!

<think>好的,用户之前问过如何在Spring Boot中使用Knife4j的依赖,现在又接着问springdoc-openapi-starter-webmvc-uispringdoc-openapi-starter-webflux-ui的区别。我需要先回顾之前的对话,确保现在的回答和之前的建议一致。 首先,用户可能正在集成Knife4j,而Knife4j依赖于SpringDoc。他们可能遇到了选择哪个SpringDoc依赖的问题,或者想了解两者的区别以便正确选择。用户可能对Spring Boot的Web框架不太熟悉,或者在构建项目时选择了错误的依赖导致出现问题。需要明确这两个依赖分别适用于不同的编程模型:WebMvc是传统的基于Servlet的同步阻塞模型,而WebFlux是响应式的非阻塞模型。 接下来,我需要详细解释两者的区别。首先,要说明它们分别支持的编程模型,即WebMvc对应的是Spring MVC,而WebFlux对应的是Spring WebFlux。然后,要提到它们的底层技术栈,比如Servlet容器与Reactive Streams。此外,需要强调在集成Knife4j时,如果用户用的是WebMvc,应该用webmvc-ui,而如果是WebFlux项目,则用webflux-ui,否则可能出问题。 还要注意版本兼容性,特别是如果用户在使用Spring Boot的不同版本,比如2.x或3.x,可能需要对应不同版本的SpringDoc。此外,用户可能关心如何判断自己的项目是哪种类型,可以提示他们检查是否引入了spring-boot-starter-web或spring-boot-starter-webflux依赖。 另外,用户可能在配置时遇到了问题,比如无法生成API文档,这时候选择错误的starter会导致功能异常。需要提醒他们在使用Knife4j时,确保SpringDocstarter与项目类型匹配,否则Knife4j可能无法正确集成。 最后,总结两者的主要区别,使用场景,并给出示例配置,帮助用户更好地理解和应用。同时,确保回答清晰,避免使用过多技术术语,让不同层次的用户都能理解。</think>在 Spring Boot 中,`springdoc-openapi-starter-webmvc-ui` 和 `springdoc-openapi-starter-webflux-ui` 都是用于生成 OpenAPI 文档的核心依赖,但它们的区别在于支持的 **编程模型** 和 **技术栈**。以下是详细对比: --- ### **1. 适用场景不同** | 依赖名称 | 适用编程模型 | 说明 | |--------------------------------------|-----------------------------|--------------------------------------------------------------------| | `springdoc-openapi-starter-webmvc-ui` | **Spring MVC**(基于 Servlet) | 适用于传统的同步阻塞式 Web 应用(如使用 `@RestController`、`@RequestMapping` 的 MVC 项目) | | `springdoc-openapi-starter-webflux-ui` | **Spring WebFlux** | 适用于响应式非阻塞 Web 应用(如使用 `RouterFunction`、`WebClient` 的 Reactive 项目) | --- ### **2. 技术栈差异** | 特性 | WebMvc UI(Servlet) | WebFlux UI(Reactive) | |------------------------|-----------------------------------|-----------------------------------| | **底层框架** | Servlet API(如 Tomcat、Jetty) | Reactive Streams(如 Netty) | | **请求处理模型** | 同步阻塞(每个请求占用一个线程) | 非阻塞异步(少量线程处理高并发请求) | | **集成方式** | 基于 `DispatcherServlet` 的路由 | 基于 `RouterFunction` 的路由 | | **依赖关系** | `spring-boot-starter-web` | `spring-boot-starter-webflux` | --- ### **3. 配置差异** #### **WebMvc 项目(传统 Servlet)** - 使用 `springdoc-openapi-starter-webmvc-ui`: ```xml <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.1.0</version> </dependency> ``` - 默认访问路径:`/swagger-ui.html` #### **WebFlux 项目(响应式)** - 使用 `springdoc-openapi-starter-webflux-ui`: ```xml <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webflux-ui</artifactId> <version>2.1.0</version> </dependency> ``` - 默认访问路径:`/webjars/swagger-ui/index.html` --- ### **4. 核心区别总结** | 对比维度 | `webmvc-ui` | `webflux-ui` | |-------------|---------------------------------|---------------------------------| | **编程模型** | Servlet(同步阻塞) | Reactive(非阻塞异步) | | **路由方式** | `@Controller` + `DispatcherServlet` | `RouterFunction` + `HandlerFilterFunction` | | **适用场景** | 传统 CRUD 应用 | 高并发、低延迟的响应式应用(如实时数据处理、流式 API) | | **依赖冲突** | 与 `webflux` 依赖不兼容 | 与 `webmvc` 依赖不兼容 | --- ### **5. 如何选择?** 1. **检查项目类型**: - 如果项目依赖中包含 `spring-boot-starter-web` → 选择 `webmvc-ui`。 - 如果项目依赖中包含 `spring-boot-starter-webflux` → 选择 `webflux-ui`。 2. **避免混合使用**: - 不要同时引入 `webmvc-ui` 和 `webflux-ui`,二者会因编程模型冲突导致启动失败。 3. **Knife4j 集成**: - Knife4j 对两者均兼容,但需要确保 SpringDoc 的依赖与项目类型匹配。 --- ### **6. 示例配置** #### **WebMvc 项目** ```yaml springdoc: swagger-ui: path: /swagger-ui.html api-docs: path: /v3/api-docs ``` #### **WebFlux 项目** ```yaml springdoc: swagger-ui: path: /swagger-ui api-docs: path: /v3/api-docs ``` --- ### **常见问题** 1. **文档无法访问**: - WebFlux 项目的 Swagger UI 默认路径可能与 WebMvc 不同,需检查配置。 - 响应式项目需要确保路由配置不拦截 `/swagger-ui/**` 路径。 2. **依赖冲突**: - 如果同时依赖 `webmvc` 和 `webflux`,Spring Boot 会优先启动 Servlet 容器,可能导致响应式特性失效。 3. **版本兼容性**: - Spring Boot 3.x(Jakarta EE)需使用 SpringDoc 2.x+ 版本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值