第 14 章 · SpringDoc OpenAPI 3 与 Swagger UI
本章目标:在 shop-spring-demo 集成 springdoc-openapi 自动生成 OpenAPI 3 规范;通过 Swagger UI 在线浏览与调试 API;为 ch13 的 DTO/VO 补充 @Schema 注解;配置分组、安全方案(Bearer JWT 占位);理解 OpenAPI 与 ch07 统一响应的文档化策略;产出可交付前端的 openapi.json。
学时建议:3~4 小时(含 1 小时文档打磨)
前置:ch13 DTO/VO;ch07 REST 统一响应;ch06 Spring Security 基础。
14.1 OpenAPI 3 与 Swagger 的关系
| 概念 | 说明 |
|---|---|
| OpenAPI Specification (OAS) | 描述 REST API 的行业标准(YAML/JSON) |
| Swagger | 围绕 OpenAPI 的工具生态(UI、Editor、Codegen) |
| SpringDoc | Spring Boot 3 生态的 OpenAPI 自动生成库(替代 springfox) |
| Swagger UI | 浏览器内嵌的交互式 API 文档与 Try it out |
shop-spring-demo Controller + DTO 注解
│
▼
springdoc-openapi 扫描
│
▼
/v3/api-docs (JSON) ──► Swagger UI (/swagger-ui.html)
│
▼
前端 SDK / Postman 导入
示例域名 https://api.example.com;文档中禁止出现真实内网地址、生产密钥或公司内部服务名。
14.2 引入依赖
pom.xml:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
启动后默认端点:
| 路径 | 说明 |
|---|---|
/swagger-ui.html | Swagger UI 入口(会重定向到 /swagger-ui/index.html) |
/v3/api-docs | OpenAPI JSON |
/v3/api-docs.yaml | OpenAPI YAML(可选) |
本地访问:http://127.0.0.1:8080/swagger-ui.html
14.3 全局 OpenAPI 配置
OpenApiConfig.java:
package com.example.shop.config;
import io.swagger.v3.oas.models.Components;
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 io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI shopOpenAPI() {
final String schemeName = "bearerAuth";
return new OpenAPI()
.info(new Info()
.title("shop-spring-demo API")
.description("虚构电商后台 REST API,教学用途")
.version("v1.0.0")
.contact(new Contact().name("Demo Team").email("api@example.com"))
.license(new License().name("MIT")))
.addSecurityItem(new SecurityRequirement().addList(schemeName))
.components(new Components()
.addSecuritySchemes(schemeName, new SecurityScheme()
.name(schemeName)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("ch15 将接入 JWT,此处为文档占位")));
}
}
application.yml 可选配置:
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
show-actuator: false
14.4 Controller 注解
@Tag(name = "商品", description = "商品 CRUD 与上下架")
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Operation(summary = "分页查询商品", description = "支持关键词搜索")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "成功",
content = @Content(schema = @Schema(implementation = ProductPageResult.class))),
@ApiResponse(responseCode = "401", description = "未登录")
})
@GetMapping
public Result<PageResult<ProductListItemVO>> list(
@Parameter(description = "页码,从 0 开始") @RequestParam(defaultValue = "0") int page,
@Parameter(description = "每页条数") @RequestParam(defaultValue = "20") int size,
@Parameter(description = "名称关键词") @RequestParam(required = false) String keyword) {
return productService.list(page, size, keyword);
}
@Operation(summary = "创建商品", description = "需要 ADMIN 角色")
@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public Result<ProductDetailVO> create(@Valid @RequestBody ProductCreateRequest req) {
return Result.ok(productService.create(req));
}
}
ProductPageResult 为文档辅助类(包装泛型):
public class ProductPageResult extends Result<PageResult<ProductListItemVO>> {}
14.5 DTO/VO 上的 @Schema
ch13 的 Bean Validation 与 @Schema 互补:
@Schema(description = "创建商品请求")
public class ProductCreateRequest {
@Schema(description = "商品名称", example = "无线鼠标", maxLength = 200)
@NotBlank(message = "商品名称不能为空")
private String name;
@Schema(description = "售价(元)", example = "99.00", minimum = "0.01")
@NotNull
private BigDecimal price;
@Schema(description = "库存数量", example = "100", minimum = "0")
@NotNull
private Integer stock;
}
@Schema(description = "商品详情视图")
public class ProductDetailVO {
@Schema(description = "商品 ID", example = "1")
private Long id;
@Schema(description = "是否已上架")
private Boolean published;
@Schema(description = "封面 URL", example = "/media/products/cover.jpg")
private String coverUrl;
}
| 注解来源 | 文档效果 |
|---|---|
@NotNull / @NotBlank | required: true |
@Size(max=200) | maxLength: 200 |
@Min / @DecimalMin | minimum |
@Schema | description、example、枚举说明 |
14.6 统一响应 Result 的文档化
ch07 的 Result<T> 包装需在文档中说明:
@Schema(description = "统一 API 响应")
public class Result<T> {
@Schema(description = "业务码,0 表示成功", example = "0")