Swagger3学习与实践指南

一、Swagger简介

出现的背景

在现代软件开发中,前后端分离的架构已成为主流。前端(Web、App)和后端(服务器)通过一套API进行通信。随着项目规模越来越大,API的数量也急剧增加,带来了一系列问题:

  • 沟通成本高:前端开发人员需要知道如何调用API,包括URL是什么、需要传什么参数、返回什么数据。后端开发人员通常通过编写Word或Markdown文档来告知前端,但这种方式效率低下且容易出错。
  • 文档维护难:代码在不断迭代,API也随之变化。如果API变更了,而文档没有及时更新,就会导致前后端联调时出现各种问题,浪费大量时间。
  • 测试不方便:开发人员需要一个简单快捷的方式来测试API接口,确保其功能符合预期。传统的测试方式通常需要借助Postman等第三方工具,操作相对繁琐。

Swagger,解决什么问题?

Swagger(现在其规范被称为 OpenAPI Specification )正是为了解决上述问题而诞生的。它是一套围绕OpenAPI规范构建的开源工具框架,可以帮助你 设计、构建、记录和使用 RESTful API

简单来说,Swagger的核心价值在于:

  • 自动生成交互式文档:只需要在代码中添加一些注解,Swagger就能自动生成一份美观、详细且可交互的API文档。前端开发者可以直接在文档页面上查看API详情并进行调用测试。
  • 保证文档与代码同步:文档是根据代码生成的,所以只要代码更新了,文档就会自动更新,彻底解决了文档与代码不一致的问题。
  • 标准化API定义:它提供了一套标准的、与语言无关的规范来描述API,使得计算机和人都能轻松理解API的功能和期望。
  • 简化团队协作:通过这份标准的、可交互的文档,前端、后端和测试人员可以高效地协作,减少沟通障碍。
  • 多语言支持:支持多种编程语言和框架

简单来说,Swagger就是让你的API文档"活"起来,永远与代码保持一致,并且可以直接在浏览器中测试。

二、Swagger2和Swagger3区别

我们通常说的Swagger 3,实际上是指实现了 OpenAPI 3.0 (OAS 3) 规范的工具。而Swagger 2对应的是旧的 Swagger 2.0 规范。OAS 3是一个重大的升级,带来了许多改进。

在Spring Boot生态中,最直观的体现是依赖库的变化:

  • Swagger 2 : 主要使用 SpringFox 库。
  • Swagger 3 (OAS 3) : 主流使用 SpringDoc 库,它与Spring Boot 3.x及更高版本兼容性更好,配置也更简单。

主要区别对比:

特性 Swagger 2 (SpringFox) Swagger 3 / OpenAPI 3 (SpringDoc) 优势说明
规范 Swagger 2.0 OpenAPI 3.0 OAS 3是当前行业标准,功能更强大。
依赖库 springfox-swagger2 springdoc-openapi-starter-webmvc-ui SpringDoc 对新版Spring Boot支持更好,配置更自动化。
组件化 定义有限,复用性一般 引入 components 对象,可轻松复用Schemas, Parameters, Responses等 结构更清晰,提高了规范的可维护性。
服务器定义 只能定义单个 hostbasePath 可定义多个 servers,每个server可以有自己的URL和描述 更灵活,轻松应对开发、测试、生产等多个环境。
请求体 使用 in: body 的参数来描述 引入独立的 requestBody 对象,可以描述更复杂的请求体和多种媒体类型 对请求体的描述更精确、更强大。
安全性 securityDefinitions securitySchemes 增强了对OAuth2和OpenID Connect的支持,描述更灵活。

结论: 如果你正在开始一个新项目,或者你的项目使用的是Spring Boot 2.x.x后期版本或3.x.x,强烈建议直接使用基于OpenAPI 3的SpringDoc。

三、集成 Swagger 3 (SpringDoc)

我们以目前最主流的SpringDoc为例,介绍如何在Spring Boot项目中集成。

1、如何集成?

集成过程非常简单,只需要一步:在你的pom.xml文件中添加依赖。

xml 复制代码
<!-- 访问 /swagger-ui.html 即可查看API文档 -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.5.0</version> <!-- 建议使用最新稳定版 -->
</dependency>

2、最小配置是什么样的?

答案是:不需要任何Java配置!一副牛B哄哄的样子

是的,你没有看错。只要你将上面的依赖添加到项目中,并重新启动你的Spring Boot应用,SpringDoc就会自动开始工作。它会:

  • 扫描项目中所有被 @RestController 注解的类。
  • 解析被 @RequestMapping, @GetMapping, @PostMapping 等注解标记的方法,生成API信息。

启动成功后,在浏览器中访问 http://localhost:8080/swagger-ui.html (端口号替换成你自己的),你就可以看到自动生成的API文档了。

访问地址:

  • Swagger UI界面http://localhost:8080/swagger-ui.html
  • API文档JSONhttp://localhost:8080/v3/api-docs

四、配置 Swagger 3

虽然零配置就能运行,但在实际项目中,我们通常需要自定义一些信息,如作者、标题、版本等,让文档更清晰。springdoc-openapi 同时支持 YML 属性配置Java 配置类 两种方式。它们各有优势,并且可以同时使用,相辅相成。

配置全局信息(标题、作者等)

这些信息可以通过项目的配置文件 application.propertiesapplication.yml 来设置。这种方式简单直观,适用于大多数情况。

使用 application.yml (推荐):

yml 复制代码
springdoc:
  api-docs:
    info:
      title: '我的项目API文档' # API文档标题,显示在Swagger UI顶部
      description: '这是一个使用Spring Boot和Swagger 3构建的示例项目。' # API详细描述,支持Markdown格式
      version: 'v1.0.0' # API版本号
      termsOfService: 'http://swagger.io/terms/'  # 服务条款URL,用户需要遵守的条款
      
      # 联系信息
      contact: 
        name: '洛小豆' # 负责人或团队名称
        email: 'luoxiaodou@juejin.com' # 作者邮箱
        url: 'https://juejin.cn/user/2049145406229127' # 联系网址或个人网站
      
      # 许可证信息
      license:
        name: 'Apache 2.0' # 使用的许可证名称(如:Apache 2.0, MIT等)
        url: 'https://www.apache.org/licenses/LICENSE-2.0.html' # 许可证详细内容的URL

使用 Java 配置类

如果希望通过编程方式来配置,可以使用一个 Java 配置类。这种方式提供了更大的灵活性,例如,你可以根据环境动态地设置不同的元数据。

你需要创建一个配置类,并使用 @Configuration@Bean 注解来定义 OpenAPI 对象。

java 复制代码
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;

/**
 * OpenAPI 配置类
 * 用于配置Swagger/OpenAPI文档生成
 */
public class OpenAPIConfig {

    /**
     * 创建自定义OpenAPI配置Bean
     * 该方法会返回一个配置好的OpenAPI实例,用于生成API文档
     *
     * @return OpenAPI 配置实例
     */
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
            // 设置API基本信息
            .info(new Info()
                // API文档标题,显示在Swagger UI顶部
                .title("我的项目API文档")
                // API详细描述,支持Markdown格式
                .description("这是一个使用Spring Boot和Swagger 3构建的示例项目。")
                // API版本号
                .version("v1.0.0")
                // 服务条款URL
                .termsOfService("http://swagger.io/terms/")
                // 设置联系信息
                .contact(new Contact()
                    // 负责人或团队名称
                    .name("洛小豆")
                    // 个人主页或项目文档链接
                    .url("https://juejin.cn/user/2049145406229127")
                    // 联系邮箱
                    .email("luoxiaodou@juejin.com"))
                // 设置许可证信息
                .license(new License()
                    // 许可证名称
                    .name("Apache 2.0")
                    // 许可证详细内容URL
                    .url("http://www.apache.org/licenses/LICENSE-2.0.html"))
            );
    }
}

配置API扫描范围

SpringDoc 提供了多种方式来精确控制哪些API需要被展示在文档中。

YML 属性配置

可以通过 springdoc.packagesToScanspringdoc.pathsToMatch 属性来配置扫描范围。

  • packagesToScan: 指定需要扫描的 Java 包名。
  • pathsToMatch: 指定需要匹配的 URL 路径模式。
  • packagesToExclude: 定义排除的包
  • pathsToExclude: 定义排除的路径
yaml 复制代码
springdoc:
  # 扫描指定包下的控制器
  packagesToScan: cn.luoxiaodou.controller
  # 或者更精确地指定多个包
  # packagesToScan: com.yourcompany.api, com.yourcompany.service.controller
  
  # 匹配指定的 URL 路径
  pathsToMatch: /api/v1/**
  # 或者匹配多个路径
  # pathsToMatch: /public/**, /private/**
  
  # 定义排除的包和路径
  # 即使某个类在 packagesToScan 范围内,如果它在 com.example.controller.internal 包下,也会被排除
  packagesToExclude: cn.luoxiaodou.controller.internal
  # 排除所有以 /public/health 开头的路径
  pathsToExclude: /public/health/**

Java 配置类

需要更精细、更灵活的控制,比如为不同的模块或版本创建独立的 API 文档,或者需要根据业务逻辑动态生成配置,那么 Java 配置类是更好的选择。

java 复制代码
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenApiConfig {

    // 全局配置:定义 API 的基本信息
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("我的应用 API 文档")
                        .description("这是一个通过 Java 配置类演示 springdoc-openapi3 的 API 文档。")
                        .version("v1.0.0"));
    }
    
    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
                .group("public-apis")  // 分组名称
                // pathsToMatch(String... pathsToMatch),这里可以输入多个值
                .pathsToMatch("/public/**") // 匹配所有以 /public/ 开头的路径
                .pathsToExclude("/public/health/**") // 排除健康检查路径
                .build();
    }
}

配置API的分组

当项目很大,API很多时,把所有API都放在一个页面上会显得很杂乱。分组功能可以让你根据模块(如用户模块、订单模块)来组织API。

这需要通过Java代码配置。创建一个配置类,并定义多个 GroupedOpenApi 类型的Bean。

java 复制代码
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SwaggerConfig {

    @Bean
    public GroupedOpenApi userApi() {
        return GroupedOpenApi.builder()
                .group("用户模块") // 分组名
                .pathsToMatch("/users/**") // 该组包含的API路径
                .build();
    }

    @Bean
    public GroupedOpenApi orderApi() {
        return GroupedOpenApi.builder()
                .group("订单模块")
                .packagesToScan("cn.luoxiaodou.project.controller.order") // 该组包含的包
                .build();
    }
    
    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
                .group("公共接口")
                .pathsToMatch("/**")
                .pathsToExclude("/users/**", "/orders/**") // 排除其他组已经包含的路径
                .build();
    }
}

配置完成后,Swagger UI页面的右上角会出现一个下拉框,你可以在不同的分组之间切换。

为什么不推荐使用 YML 配置进行分组?

虽然你可以通过 YML 配置来扫描和排除路径,但这只能实现单个、全局的配置,无法像 GroupedOpenApi 那样创建多个独立的、可切换的分组。如果你只在乎扫描范围,YML 足够了;但如果你的目标是更好地组织和呈现文档,Java 配置类是实现分组的唯一推荐方式。


多环境配置,启用/禁用Swagger

在使用 springdoc-openapi3 时,有时你需要根据不同的环境(例如开发、测试或生产环境)来启用或禁用 Swagger 文档。

YML 属性配置

这是最常用、最简单的配置方式。你可以通过设置 springdoc.swagger-ui.enabled 属性来控制 Swagger UI 的启用或禁用。

yaml 复制代码
# 在开发和测试环境中启用 Swagger UI
springdoc:
  swagger-ui:
    enabled: true
  api-docs:
    enabled: true

# 在生产环境中禁用 Swagger UI
springdoc:
  swagger-ui:
    enabled: false
  api-docs:
    enabled: false

通常,我们会将这个配置放在不同的配置文件中,例如 application-dev.yml(启用)和 application-prod.yml(禁用),这样 Spring Boot 会根据你的活动 profile 自动加载对应的配置。

Java 配置类

如果你需要更复杂的逻辑来决定是否启用 Swagger,比如根据某些自定义条件,那么可以使用 Java 配置类。

你可以创建一个配置类,并利用 @ConditionalOnProperty 注解来实现条件启用。

java 复制代码
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(name = "springdoc.swagger-ui.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerEnableConfig {

    // 这个配置类只有在 springdoc.swagger-ui.enabled=true 时才会被加载
    // matchIfMissing = true 意味着如果该属性不存在,则默认启用
}

实体类(Model)配置

Swagger会自动扫描你在API中用作参数(@RequestBody)或返回值(ResponseEntity)的Java实体类,并在文档的 "Schemas" 部分展示其结构。

我们可以通过注解来丰富实体类的信息。

  • @Schema: 这是最核心的注解,用于描述一个实体类或其字段。

    • description: 描述信息。
    • example: 示例值。
    • requiredMode: 字段是否必须。RequiredMode.REQUIRED (必须), RequiredMode.NOT_REQUIRED (非必须)。
    • defaultValue: 默认值。

@Schema注解详细使用代码

java 复制代码
@Schema(
    description = "用户详细信息",           // 描述
    example = "User object example",       // 示例说明
    title = "User",                        // 标题
    requiredProperties = {"username", "email"} // 必填字段
)
public class UserDetail {
    
    @Schema(
        description = "用户唯一标识",       // 字段描述
        example = "1001",                  // 示例值
        required = true,                   // 是否必填
        minimum = "1",                     // 最小值
        maximum = "999999"                 // 最大值
    )
    private Long id;
    
    @Schema(
        description = "用户名",
        example = "zhangsan",
        required = true,
        minLength = 3,                     // 最小长度
        maxLength = 20,                    // 最大长度
        pattern = "^[a-zA-Z0-9_]+$"        // 正则表达式
    )
    private String username;
    
    @Schema(
        description = "用户类型",
        example = "NORMAL",
        allowableValues = {"NORMAL", "VIP", "ADMIN"}  // 允许的值
    )
    private UserType userType;
    
    @Schema(description = "敏感信息", hidden = true)  // 隐藏字段
    private String password;
    
    @Schema(
        description = "用户标签",
        example = "["新用户", "活跃用户"]"
    )
    private List<String> tags;
}

验证注解结合使用

java 复制代码
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

@Schema(description = "用户注册请求")
public class UserRegisterRequest {
    
    @Schema(description = "用户名", example = "zhangsan")
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
    private String username;
    
    @Schema(description = "密码", example = "123456")
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, message = "密码长度至少6位")
    private String password;
    
    @Schema(description = "邮箱", example = "zhangsan@example.com")
    @Email(message = "邮箱格式不正确")
    @NotBlank(message = "邮箱不能为空")
    private String email;
    
    @Schema(description = "年龄", example = "25")
    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150")
    private Integer age;
}

注意SpringDoc 会自动识别 jakarta.validation.constraints (或 javax.validation.constraints) 包下的注解,如 @NotBlank, @NotNull, @Size 等,并自动将字段标记为必填项和添加校验规则说明。

五、注解汇总

控制器类注解:

注解 作用 常用属性 示例
@Tag 给控制器添加标签和描述 name, description 标识API分组
@Hidden 隐藏整个控制器 - 不在文档中显示

使用示例:

java 复制代码
@Tag(name = "用户管理", description = "用户相关的增删改查操作")
@RestController
@RequestMapping("/api/user")
public class UserController {
    // 控制器方法
}

方法级注解

注解 作用 常用属性 示例
@Operation 描述API操作 summary, description, tags 方法说明
@Parameter 描述参数 name, description, required, example 参数说明
@Parameters 多个参数描述 value 包含多个@Parameter
@ApiResponse 描述响应 responseCode, description 响应说明
@ApiResponses 多个响应描述 value 包含多个@ApiResponse
@Hidden 隐藏方法 - 不在文档中显示

详细使用示例

java 复制代码
@Tag(name = "用户管理", description = "用户相关操作")
@RestController
@RequestMapping("/api/user")
public class UserController {
    
    @Operation(
        summary = "获取用户列表",
        description = "分页获取系统中所有用户的信息,支持按条件筛选",
        tags = {"用户查询"}
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "获取成功"),
        @ApiResponse(responseCode = "400", description = "请求参数错误"),
        @ApiResponse(responseCode = "500", description = "服务器内部错误")
    })
    @GetMapping("/list")
    public PageResult<User> getUserList(
        @Parameter(description = "页码", example = "1", required = true)
        @RequestParam(defaultValue = "1") Integer page,
        
        @Parameter(description = "每页大小", example = "10", required = true)
        @RequestParam(defaultValue = "10") Integer size,
        
        @Parameter(description = "用户名搜索关键词", example = "zhang")
        @RequestParam(required = false) String keyword
    ) {
        // 业务逻辑
        return userService.findUsers(page, size, keyword);
    }
    
    @Operation(summary = "创建用户", description = "创建新的用户账户")
    @ApiResponses({
        @ApiResponse(responseCode = "201", description = "创建成功"),
        @ApiResponse(responseCode = "400", description = "用户信息验证失败"),
        @ApiResponse(responseCode = "409", description = "用户名或邮箱已存在")
    })
    @PostMapping("/create")
    public User createUser(
        @Parameter(description = "用户信息", required = true)
        @Valid @RequestBody UserCreateRequest request
    ) {
        return userService.createUser(request);
    }
    
    @Operation(summary = "更新用户信息")
    @PutMapping("/{id}")
    public User updateUser(
        @Parameter(description = "用户ID", example = "1001", required = true)
        @PathVariable Long id,
        
        @Parameter(description = "更新的用户信息", required = true)
        @Valid @RequestBody UserUpdateRequest request
    ) {
        return userService.updateUser(id, request);
    }
    
    @Operation(summary = "删除用户")
    @DeleteMapping("/{id}")
    public void deleteUser(
        @Parameter(description = "要删除的用户ID", example = "1001", required = true)
        @PathVariable Long id
    ) {
        userService.deleteUser(id);
    }
    
    @Hidden  // 隐藏此接口,不在文档中显示
    @GetMapping("/internal")
    public String internalApi() {
        return "内部接口";
    }
}

实体类注解(重要):

注解 作用 常用属性 示例
@Schema 描述实体类或字段 description, example, required 实体说明
@JsonIgnore 忽略字段 - 隐藏敏感信息
@JsonProperty 自定义字段名 value 字段重命名
java 复制代码
// 请求实体
@Schema(description = "用户创建请求")
public class UserCreateRequest {
    
    @Schema(description = "用户名", example = "zhangsan", required = true)
    @NotBlank(message = "用户名不能为空")
    private String username;
    
    @Schema(description = "邮箱", example = "zhangsan@example.com", required = true)
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Schema(description = "年龄", example = "25")
    @Min(0) @Max(150)
    private Integer age;
}

// 响应实体
@Schema(description = "分页结果")
public class PageResult<T> {
    
    @Schema(description = "数据列表")
    private List<T> data;
    
    @Schema(description = "总记录数", example = "100")
    private Long total;
    
    @Schema(description = "当前页码", example = "1")
    private Integer page;
    
    @Schema(description = "每页大小", example = "10")
    private Integer size;
}

wagger2兼容注解

Swagger3仍然支持Swagger2的注解,但建议使用新的OpenAPI 3注解:

Swagger2注解 Swagger3替代注解 说明
@Api @Tag 控制器描述
@ApiOperation @Operation 方法描述
@ApiParam @Parameter 参数描述
@ApiModel @Schema 实体类描述
@ApiModelProperty @Schema 实体字段描述
@ApiResponse @ApiResponse 响应描述(相同)
@ApiIgnore @Hidden 隐藏接口

完整的最佳实践示例

java 复制代码
/**
 * 商品管理控制器
 */
@Tag(name = "商品管理", description = "商品的增删改查操作")
@RestController
@RequestMapping("/api/product")
@Validated
public class ProductController {
    
    @Autowired
    private ProductService productService;
    
    @Operation(
        summary = "获取商品详情",
        description = "根据商品ID获取商品的详细信息,包括库存、价格等"
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "获取成功"),
        @ApiResponse(responseCode = "404", description = "商品不存在"),
        @ApiResponse(responseCode = "500", description = "服务器错误")
    })
    @GetMapping("/{id}")
    public Product getProductById(
        @Parameter(description = "商品ID", example = "1001", required = true)
        @PathVariable Long id
    ) {
        return productService.findById(id);
    }
    
    @Operation(
        summary = "搜索商品",
        description = "根据关键词、分类、价格区间等条件搜索商品"
    )
    @GetMapping("/search")
    public PageResult<Product> searchProducts(
        @Parameter(description = "搜索关键词", example = "手机")
        @RequestParam(required = false) String keyword,
        
        @Parameter(description = "商品分类ID", example = "1")
        @RequestParam(required = false) Long categoryId,
        
        @Parameter(description = "最低价格", example = "100.00")
        @RequestParam(required = false) BigDecimal minPrice,
        
        @Parameter(description = "最高价格", example = "5000.00")
        @RequestParam(required = false) BigDecimal maxPrice,
        
        @Parameter(description = "页码", example = "1")
        @RequestParam(defaultValue = "1") @Min(1) Integer page,
        
        @Parameter(description = "每页大小", example = "20")
        @RequestParam(defaultValue = "20") @Min(1) @Max(100) Integer size
    ) {
        return productService.searchProducts(keyword, categoryId, minPrice, maxPrice, page, size);
    }
    
    @Operation(
        summary = "创建商品",
        description = "创建新的商品信息,需要管理员权限"
    )
    @ApiResponses({
        @ApiResponse(responseCode = "201", description = "创建成功"),
        @ApiResponse(responseCode = "400", description = "商品信息验证失败"),
        @ApiResponse(responseCode = "403", description = "权限不足")
    })
    @PostMapping
    public Product createProduct(
        @Parameter(description = "商品信息", required = true)
        @Valid @RequestBody ProductCreateRequest request
    ) {
        return productService.createProduct(request);
    }
    
    @Operation(summary = "批量上传商品图片")
    @PostMapping("/{id}/images")
    public List<String> uploadProductImages(
        @Parameter(description = "商品ID", required = true)
        @PathVariable Long id,
        
        @Parameter(description = "商品图片文件", required = true)
        @RequestParam("files") MultipartFile[] files
    ) {
        return productService.uploadImages(id, files);
    }
    
    @Hidden  // 管理员内部接口,不对外显示
    @DeleteMapping("/batch")
    public void batchDeleteProducts(@RequestBody List<Long> ids) {
        productService.batchDelete(ids);
    }
}

/**
 * 商品信息实体类
 */
@Schema(description = "商品信息")
public class Product {
    
    @Schema(description = "商品ID", example = "1001")
    private Long id;
    
    @Schema(description = "商品名称", example = "iPhone 15 Pro", required = true)
    private String name;
    
    @Schema(description = "商品描述", example = "最新款iPhone,配备A17芯片")
    private String description;
    
    @Schema(description = "商品价格", example = "7999.00", required = true)
    private BigDecimal price;
    
    @Schema(description = "库存数量", example = "100")
    private Integer stock;
    
    @Schema(description = "商品分类ID", example = "1")
    private Long categoryId;
    
    @Schema(description = "商品分类名称", example = "智能手机")
    private String categoryName;
    
    @Schema(description = "商品图片URL列表")
    private List<String> imageUrls;
    
    @Schema(description = "商品状态", example = "ACTIVE", 
            allowableValues = {"ACTIVE", "INACTIVE", "SOLD_OUT"})
    private ProductStatus status;
    
    @Schema(description = "商品标签", example = "["热销", "新品"]")
    private List<String> tags;
    
    @Schema(description = "创建时间", example = "2023-12-01T10:30:00")
    private LocalDateTime createTime;
    
    @Schema(description = "更新时间", example = "2023-12-01T10:30:00")
    private LocalDateTime updateTime;
    
    @JsonIgnore  // 不在API文档中显示
    private String internalCode;
    
    // getter和setter方法省略...
}

/**
 * 商品创建请求
 */
@Schema(description = "商品创建请求")
public class ProductCreateRequest {
    
    @Schema(description = "商品名称", example = "iPhone 15 Pro", required = true)
    @NotBlank(message = "商品名称不能为空")
    @Size(max = 100, message = "商品名称长度不能超过100字符")
    private String name;
    
    @Schema(description = "商品描述", example = "最新款iPhone")
    @Size(max = 1000, message = "商品描述长度不能超过1000字符")
    private String description;
    
    @Schema(description = "商品价格", example = "7999.00", required = true)
    @NotNull(message = "商品价格不能为空")
    @DecimalMin(value = "0.01", message = "商品价格必须大于0")
    @Digits(integer = 10, fraction = 2, message = "价格格式不正确")
    private BigDecimal price;
    
    @Schema(description = "库存数量", example = "100", required = true)
    @NotNull(message = "库存数量不能为空")
    @Min(value = 0, message = "库存数量不能小于0")
    private Integer stock;
    
    @Schema(description = "商品分类ID", example = "1", required = true)
    @NotNull(message = "商品分类不能为空")
    private Long categoryId;
    
    @Schema(description = "商品标签", example = "["热销", "新品"]")
    private List<String> tags;
}

六、常见问题和解决方案

问题1:中文乱码

yml 复制代码
# application.yml
server:
  servlet:
    encoding:
      charset: UTF-8
      force: true

springdoc:
  api-docs:
    path: /v3/api-docs
  swagger-ui:
    path: /swagger-ui.html
    # 解决中文显示问题
    config-url: /v3/api-docs/swagger-config

问题2:接口返回类型显示不正确

java 复制代码
// 错误方式:返回类型模糊
@GetMapping("/users")
public Object getUsers() { ... }

// 正确方式:明确返回类型
@GetMapping("/users")
public ApiResult<List<User>> getUsers() { ... }

// 或者使用@Operation注解明确指定
@Operation(summary = "获取用户列表")
@ApiResponse(responseCode = "200", 
             description = "成功", 
             content = @Content(schema = @Schema(implementation = User.class)))
@GetMapping("/users")
public Object getUsers() { ... }

问题3:枚举类型显示优化

java 复制代码
@Schema(description = "用户状态")
public enum UserStatus {
    
    @Schema(description = "正常")
    ACTIVE("1", "正常"),
    
    @Schema(description = "禁用")
    INACTIVE("0", "禁用"),
    
    @Schema(description = "锁定")
    LOCKED("2", "锁定");
    
    private String code;
    private String description;
    
    UserStatus(String code, String description) {
        this.code = code;
        this.description = description;
    }
    
    // getter方法...
}
相关推荐
天若有情6731 小时前
【java EE】IDEA 中创建或迁移 Spring 或 Java EE 项目的核心步骤和注意事项
后端·spring·java-ee·intellij-idea
大鱼七成饱3 小时前
💥 从崩溃到稳定:我踩过的 Rust Tokio 线程池坑(含代码示例)
后端
喵个咪3 小时前
开箱即用的GO后台管理系统 Kratos Admin - 站内信
后端·微服务·go
小雨的光3 小时前
QuickEsView
spring boot·elasticsearch·es可视化
韩立学长3 小时前
基于Springboot的旧物公益捐赠管理系统3726v22v(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
Dyan_csdn3 小时前
springboot系统设计选题3
java·spring boot·后端
Yeats_Liao4 小时前
时序数据库系列(二):InfluxDB安装配置从零搭建
数据库·后端·时序数据库
Yeats_Liao4 小时前
时序数据库系列(一):InfluxDB入门指南核心概念详解
数据库·后端·时序数据库·db
蓝-萧5 小时前
springboot系列--自动配置原理
java·后端
bobogift5 小时前
【玩转全栈】----Django基本配置和介绍
java·后端