返回

Maven 生成 OpenAPI 3.0 (Swagger):Spring 项目指南

java

Maven 编译时从 Spring 应用源码生成 OpenAPI 3.0 定义文件

在使用 Spring 框架(非 Spring Boot)开发 RESTful API 时,我们常常需要生成 API 文档。OpenAPI(Swagger)是一个流行的 API 文档规范。本文将介绍如何使用 Maven 插件,在编译阶段从 Spring 应用源码中提取信息,自动生成 OpenAPI 3.0 定义文件(JSON 或 YAML 格式)。

碰到的问题

你已经在 Controller 类中添加了 io.swagger.v3.oas.annotations 注解,例如:

package com.acme.rest;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "Dummy Controller", description = "Dummy controller.")
@RestController
@RequestMapping("/api/v1/dummy")
public class DummyController {

    @Operation(summary = "dummy(). Does litrally nothing.")
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String doStuff() {
        return "dummy";
    }
}

并且尝试使用了 swagger-maven-plugin

<plugin>
    <groupId>io.swagger.core.v3</groupId>
    <artifactId>swagger-maven-plugin</artifactId>
    <version>2.2.0</version>
    <configuration>
        <outputPath>${project.build.directory}/swagger-def</outputPath>
        <resourcePackages>com.acme</resourcePackages>
        <prettyPrint>true</prettyPrint>
    </configuration>
    <executions>
        <execution>
            <phase>compile</phase>
            <goals>
                <goal>resolve</goal>
            </goals>
        </execution>
    </executions>
</plugin>

但是,执行 mvn clean compile 后,生成的 OpenAPI 定义文件只包含版本信息:

{
  "openapi" : "3.0.1"
}

问题在于,swagger-maven-plugin 默认情况下不会自动扫描和解析 io.swagger.v3.oas.annotations 注解。 尽管注解和插件都来自io.swagger.core.v3 组。 除非提供了自定义的io.swagger.v3.oas.integration.api.OpenApiReader 和/或 io.swagger.v3.oas.integration.api.OpenApiScanner 实现, 否则它不会处理。

问题原因分析

swagger-maven-plugin 的核心逻辑依赖于 OpenApiReaderOpenApiScanner 接口的实现。 这两个接口负责:

  • OpenApiScanner: 扫描项目中的类,识别出需要纳入 OpenAPI 定义的资源(例如,带有 @RestController 注解的类)。
  • OpenApiReader: 读取类和方法上的 Swagger 注解(例如,@Operation, @Tag),并将其转换为 OpenAPI 定义对象。

swagger-maven-plugin 默认提供了一个扫描器 DefaultJaxrsScanner, 从名称就能推断它用于jax-rs, 而不是spring。 不指定自定义实现的话,插件就无法正确解析我们项目中的 Spring MVC 注解。

解决方案

方案一:提供自定义的 Scanner 和 Reader (麻烦, 不建议)

根据 官方文档 的建议, 可以添加它们的自定义实现.

<scannerClass>com.acme.util.SwaggerOpenApiScanner</scannerClass>
<readerClass>com.acme.util.SwaggerOpenApiReader</readerClass>

但老实讲, 这很不方便.

方案二: 使用 springdoc-openapi-maven-plugin(推荐!)

springdoc-openapi-maven-plugin 是一个专门为 Spring 项目设计的 OpenAPI 生成插件,它能很好地与 Spring MVC 集成,自动识别和处理 Spring 的相关注解。

  1. 添加插件依赖:

    pom.xml 文件的 plugins 部分添加以下配置:

    <plugin>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-maven-plugin</artifactId>
        <version>0.3</version>
        <executions>
            <execution>
                <id>integration-test</id>
                <goals>
                    <goal>generate</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
             <apiDocsUrl>http://localhost:8080/v3/api-docs</apiDocsUrl>
             <outputFileName>openapi.json</outputFileName>
             <outputDir>${project.build.directory}</outputDir>
        </configuration>
    </plugin>
    
    • springdoc-openapi-maven-plugin: 插件的坐标。
    • version: 插件版本。可以去maven 仓库里面找最新的稳定版。
    • generate: 插件目标,用于生成 OpenAPI 定义。
    • <apiDocsUrl>: 指向你的 Spring 应用程序的 API 文档端点。 通常,Springdoc 默认的 API 文档路径是 /v3/api-docs你需要启动你的 Spring 应用程序才能生成。
    • <outputFileName>: 指定生成的 OpenAPI 定义文件的名称(可选, 默认 openapi.json)。
    • <outputDir>: 指定生成的 OpenAPI 定义文件的输出目录(可选,默认 ${project.build.directory})。
  2. 运行插件:

    首先启动你的 Spring 应用程序 (为了springdoc-openapi-maven-plugin可以访问)。 然后运行以下 Maven 命令:

    mvn clean install
    

    生成的 openapi.json(或你在 <outputFileName> 中指定的名称)文件会出现在你配置的 <outputDir> 中(默认是 target 目录)。

原理和作用:

springdoc-openapi-maven-plugin 的工作原理是这样的: 它会尝试访问你指定的 apiDocsUrl,这个 URL 应该是你的 Spring 应用提供的 OpenAPI 定义的访问地址。springdoc-openapi 库(你的 Spring 项目需要集成它)会自动根据你代码中的 Spring MVC 注解和 Swagger 注解生成 OpenAPI 定义,然后通过这个 URL 暴露出来。 springdoc-openapi-maven-plugin拿到定义后再把它保存到本地文件中。

额外说明:

  • 你的项目需要集成 springdoc-openapi 库, 使你的应用可以通过 apiDocsUrl 访问 OpenAPI. 参考下方**"进阶: 搭配springdoc-openapi-ui 使用"** 。
  • 确保 apiDocsUrl 正确指向你的应用, 且你的应用处于运行状态.
  • 这种方法是生成一个静态的 OpenAPI 文件,这个文件与实际运行的 Spring 应用程序的状态是分离的。这意味着,如果在生成文件后修改了代码,需要重新运行 Maven 插件来更新这个文件。

进阶: 搭配springdoc-openapi-ui 使用

为了使用 springdoc-openapi-maven-plugin, 你的 Spring 项目应该能够通过某个 URL 来访问OpenAPI, springdoc-openapi-ui 就能完成这项工作:

  1. 添加依赖:
    非 Spring Boot项目, 你应该按如下所示,手动将不同功能的 starter 加入依赖.

       <dependency>
           <groupId>org.springdoc</groupId>
           <artifactId>springdoc-openapi-webmvc-core</artifactId>
           <version>1.7.0</version>
       </dependency>
       <dependency>
           <groupId>org.springdoc</groupId>
           <artifactId>springdoc-openapi-ui</artifactId>
           <version>1.7.0</version>
      </dependency>
    

    去 maven 仓库查询并使用最新的稳定版。

  2. 配置(如果必要):

    通常情况下,springdoc-openapi-ui 不需要额外配置就可以工作。 它会自动扫描你的 Spring MVC 控制器和相关的 Swagger 注解。

    但如果你有特殊需求,例如自定义 API 文档的路径、分组等,可以在你的 Spring 配置文件(例如,application.properties 或 XML 配置文件)中进行设置。

举例(在XML 配置中):
  ```xml
  <bean class="org.springdoc.core.GroupedOpenApi" id="publicApi">
        <constructor-arg index="0" value="public"/>
        <constructor-arg index="1">
            <bean class="org.springdoc.core.GroupedOpenApi$Builder">
                <property name="group" value="users"/>
                <property name="pathsToMatch" value="/api/v1/user/**"/>
            </bean>
        </constructor-arg>
  </bean>
  ```
  1. 启动应用, 就可以使用默认路径访问了 http://localhost:8080/swagger-ui.html

####方案三:自定义插件 (作为终极方案)
自己开发一个插件去实现OpenApiReaderOpenApiScanner工作, 原理还是根据指定的 resourcePackages 路径去扫描和读取 class.
虽然需要对 maven 插件开发比较熟悉, 不过作为终极方案可以了解. 这里不赘述了.

总结

遇到类似的问题要善用搜索引擎与官网, 一些陈旧的方法可能已经不再适用. 比如本次 swagger-maven-plugin就不如springdoc-openapi-maven-plugin 更方便好用。 希望以上解决方案能解决你的困惑。