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

GraalVM Native Image无法找到FatJar中依赖库问题求助

问题:FatJar构建成功但GraalVM Native Image生成时找不到Clikt依赖类的解决方法

环境信息

macOS Sonoma 14.5上的GraalVM版本:

java -version
java version "21" 2023-09-19
Java(TM) SE Runtime Environment Oracle GraalVM 21+35.1 (build 21+35-jvmci-23.1-b15)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21+35.1 (build 21+35-jvmci-23.1-b15, mixed mode, sharing)

Gradle版本:

gradle --v

------------------------------------------------------------
Gradle 8.7
------------------------------------------------------------

Build time:   2024-03-22 15:52:46 UTC
Revision:     650af14d7653aa949fce5e886e685efc9cf97c10

Kotlin:       1.9.22
Groovy:       3.0.17
Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM:          21 (Oracle Corporation 21+35-jvmci-23.1-b15)
OS:           Mac OS X 14.5 aarch64

build.gradle.kts配置:

plugins {
    kotlin("jvm") version "1.9.23"
}

group = "com.example"
version = "1.0-SNAPSHOT"

tasks.withType<Jar> {
    manifest {
        attributes["Main-Class"] = "com.example.SampleCli"
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.github.ajalt.clikt:clikt:4.3.0")
    testImplementation(kotlin("test"))
}

tasks.test {
    useJUnitPlatform()
}
kotlin {
    jvmToolchain(21)
}

fun getClasspath() = "libs/" + file("$buildDir/libs").list()?.joinToString(":libs/")

task<Exec>("buildNativeImage") {
    description = "Build native image using GraalVM"
    dependsOn(tasks.getByName("build"))
    workingDir(buildDir)
    commandLine(
        "native-image",
        "-cp", getClasspath(),
        "-H:+ReportExceptionStackTraces",
        "-H:+AddAllCharsets",
        // "--report-unsupported-elements-at-runtime",
        // "--allow-incomplete-classpath",
        "--no-server",
        "--no-fallback",
        "--enable-http",
        "--enable-https",
        "com.example.SampleCli",
        "com.example"
    )
}

主类代码:

package com.example

import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.help
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.prompt
import com.github.ajalt.clikt.parameters.types.int

class SampleCli() : CliktCommand(name="cli") {

    val count: Int by option().int().default(1).help("Number of greetings")
    val name: String by option().prompt("Your name").help("The person to greet")

    override fun run() {
        repeat(count) {
            echo("Hello $name!")
        }
    }
}

fun main(args: Array<String>) = SampleCli().main(args)

问题现象

执行./gradlew build构建项目时,输出:

BUILD SUCCESSFUL in 647ms
3 actionable tasks: 3 executed

但执行GraalVM Native Image生成命令(或./gradlew buildNativeImage)时失败,执行的命令:

native-image -jar build/libs/SampleCli-1.0-SNAPSHOT.jar cli

报错信息:

========================================================================================================================
GraalVM Native Image: Generating 'cli' (executable)...
========================================================================================================================
[1/8] Initializing...                                                                                    (0.0s @ 0.15GB)

The build process encountered an unexpected error:

> java.lang.NoClassDefFoundError: com/github/ajalt/clikt/core/CliktCommand

Please inspect the generated error report at:
/Users/user1/SampleProject/svm_err_b_20240603T04124.720_pid14052.md

If you are unable to resolve this problem, please file an issue with the error report at:
https://graalvm.org/support

原因分析

  1. 现有Jar任务生成的是普通Jar包,而非包含所有依赖的FatJar(uber-Jar),因此Jar包中只包含项目自身代码,没有Clikt等第三方依赖类。
  2. buildNativeImage任务中的getClasspath()函数逻辑错误:它试图从build/libs目录下找依赖Jar,但Gradle默认不会把依赖包复制到该目录,导致类路径不完整。

解决步骤

步骤1:修改Gradle配置,生成FatJar

替换原有的tasks.withType<Jar>配置,使用shadowJar插件生成包含所有依赖的FatJar:

plugins {
    kotlin("jvm") version "1.9.23"
    id("com.github.johnrengelman.shadow") version "8.1.1" // 添加shadow插件
}

// 替换原Jar任务配置,用shadowJar配置主类
tasks.shadowJar {
    manifest {
        attributes["Main-Class"] = "com.example.SampleCli"
    }
    archiveBaseName.set("SampleCli") // 自定义Jar包名称
    archiveVersion.set("1.0-SNAPSHOT")
}

// 让build任务依赖shadowJar,确保构建时生成FatJar
tasks.build {
    dependsOn(tasks.shadowJar)
}

步骤2:修复buildNativeImage任务的类路径逻辑

修改getClasspath()函数,或者直接使用FatJar路径作为类路径:

task<Exec>("buildNativeImage") {
    description = "Build native image using GraalVM"
    dependsOn(tasks.shadowJar) // 依赖shadowJar而非build
    val shadowJar = tasks.shadowJar.get()
    commandLine(
        "native-image",
        "-jar", shadowJar.archiveFile.get().asFile.absolutePath, // 直接使用FatJar
        "-H:+ReportExceptionStackTraces",
        "-H:+AddAllCharsets",
        "--no-server",
        "--no-fallback",
        "--enable-http",
        "--enable-https",
        "cli" // 生成的可执行文件名称
    )
}

步骤3:重新构建并生成Native Image

执行以下命令:

./gradlew buildNativeImage

额外说明

  • 使用shadowJar插件是Gradle生成FatJar的标准方式,能自动打包所有依赖到单个Jar包中。
  • 直接用-jar参数指定FatJar给native-image命令,无需手动拼接类路径,避免路径错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:43:10