You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3.x下创建Gradle任务导出OpenAPI规范至build目录失败求助

解决Spring Boot 3.0.6 + springdoc 2.1.0下Gradle生成OpenAPI规范文件到build目录的问题

方案一:使用适配Spring Boot 3的springdoc Gradle插件

你之前用的org.springdoc.openapi-gradle-plugin:1.6.0仅支持Spring Boot 2.x,Spring Boot 3需要搭配2.x版本的插件,步骤如下:

  1. 在settings.gradle中确保插件仓库配置正确:
pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
}
  1. 在build.gradle引入适配版本的插件:
plugins {
    id 'org.springframework.boot' version '3.0.6'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
    // 适配Spring Boot 3的springdoc插件版本
    id 'org.springdoc.openapi-gradle-plugin' version '2.1.0'
}
  1. 配置OpenAPI生成任务:
openApi {
    apiDocsUrl = "http://localhost:8080/v3/api-docs"
    outputDir = file("$buildDir/generated-specs")
    outputFileName = "openapi.json"
    waitTimeInSeconds = 10 // 预留应用启动等待时间
}
  1. 执行任务:
./gradlew openApi

该任务会自动启动Spring Boot应用、等待服务就绪、下载规范文件后停止应用。

方案二:自定义Gradle任务(无需插件)

如果不想依赖插件,可以手动实现"启动应用→等待就绪→下载规范→停止应用"的流程:

// 后台启动Spring Boot应用
task startAppInBackground {
    dependsOn classes
    doLast {
        def process = javaexec {
            classpath = sourceSets.main.runtimeClasspath
            main = 'com.telekom.onecpq.bffcatalog.CatalogApplication'
            jvmArgs '-Dspring.application.admin.enabled=true' // 启用Admin端点方便停止应用
            daemon = true // 后台运行
        }
        project.ext.appProcess = process // 保存进程引用
    }
}

// 轮询等待应用服务就绪
task waitForAppReady {
    dependsOn startAppInBackground
    doLast {
        def maxAttempts = 30
        def attempt = 0
        def apiUrl = "http://localhost:8080/v3/api-docs"
        
        while (attempt < maxAttempts) {
            try {
                new URL(apiUrl).openConnection().with {
                    connectTimeout = 1000
                    connect()
                    println "应用服务已就绪"
                    return
                }
            } catch (Exception e) {
                println "等待应用启动中... 第${attempt+1}次尝试"
                Thread.sleep(1000)
                attempt++
            }
        }
        if (attempt >= maxAttempts) {
            throw new GradleException("应用启动超时,无法连接到${apiUrl}")
        }
    }
}

// 下载OpenAPI规范到build目录
task downloadOpenApiSpec {
    dependsOn waitForAppReady
    doLast {
        def outputDir = file("$buildDir/generated-specs")
        outputDir.mkdirs()
        def outputFile = file("$outputDir/openapi.json")
        def apiUrl = "http://localhost:8080/v3/api-docs"
        
        // 用Groovy内置HTTP客户端下载文件
        new URL(apiUrl).openStream().with { input ->
            outputFile.withOutputStream { output ->
                output << input
            }
        }
        println "OpenAPI规范已保存到: ${outputFile.absolutePath}"
    }
}

// 停止后台运行的应用
task stopApp {
    dependsOn downloadOpenApiSpec
    doLast {
        def appProcess = project.ext.appProcess
        if (appProcess != null && appProcess.isAlive()) {
            try {
                // 通过Spring Boot Admin端点优雅停止应用
                new URL("http://localhost:8080/actuator/shutdown").openConnection().with {
                    requestMethod = "POST"
                    connect()
                }
                println "应用已优雅停止"
            } catch (Exception e) {
                // 优雅停止失败则直接销毁进程
                appProcess.destroy()
                println "通过进程销毁停止应用"
            }
        }
    }
}

// 组合任务,一键执行全流程
task generateOpenApiSpec {
    dependsOn stopApp
    group = "documentation"
    description = "启动应用、下载OpenAPI规范并停止应用"
}

执行组合任务:

./gradlew generateOpenApiSpec

你之前的任务失效原因

  • 第一个JavaExec任务:org.springdoc.api.OpenApiResource是Spring MVC控制器类,并非可执行主类,直接运行无法生成规范。
  • 第二个downloadFile任务:bootRun是阻塞任务,会一直占用进程运行应用,后续的curl命令永远不会执行。
  • 第三个downloadFile1任务:startApplication任务是阻塞的,应用启动后会持续运行,downloadFile1需等待其结束才会执行,因此无法触发下载。

内容的提问来源于stack exchange,提问作者Denis Murashko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 19:40:01