Spring Boot Kotlin项目Jar执行报NoClassDefFoundError问题求助
通过Spring Initializr创建了一个Spring Boot项目,构建后的Jar文件无法执行,报错如下:
java -jar build/libs/spring302-0.0.1-SNAPSHOT-plain.jar Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics at com.kaede.spring302.Spring302ApplicationKt.main(Spring302Application.kt) Caused by: java.lang.ClassNotFoundException: kotlin/jvm/internal/Intrinsics at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ... 1 more
需要解决该问题以将项目构建为Docker镜像,请问该如何处理?
选择的配置如下:
- Kotlin
- Gradle 7.2
- Spring 3.0.2
- Java 17
使用IntelliJ进行构建,项目名称为spring302,命名空间为com.kaede。
1. 添加Manifest主类配置
最初直接构建后执行Jar报错:
java -jar build/libs/kotlin1-0.0.1-SNAPSHOT-plain.jar Error: Could not find or load main class
于是在spring302/build.gradle.kts中添加Manifest主类配置:
tasks.withType<Jar> { manifest { attributes["Main-Class"] = "com.kaede.spring302.Spring302Application" } }
执行后又报错:
java -jar build/libs/spring302-0.0.1-SNAPSHOT-plain.jar Error: Main method not found in class com.kaede.spring302.Spring302Application, please define the main method as: public static void main(String[] args)
提示找不到符合Java规范的main方法。
2. 修改主类为classNameKt
了解到Kotlin类编译后会生成classNameKt类,于是修改主类配置:
tasks.withType<Jar> { manifest { attributes["Main-Class"] = "com.kaede.spring302.Spring302ApplicationKt" } }
执行后出现NoClassDefFoundError:
java -jar build/libs/spring302-0.0.1-SNAPSHOT-plain.jar Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics at com.kaede.spring302.Spring302ApplicationKt.main(Spring302Application.kt) Caused by: java.lang.ClassNotFoundException: kotlin/jvm/internal/Intrinsics at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ... 1 more
通过javap命令确认该类存在main方法:
javap spring302-0.0.1-SNAPSHOT-plain/com/kaede/spring302/Spring302ApplicationKt.class Compiled from "Spring302Application.kt" public final class com.kaede.spring302.Spring302ApplicationKt { public static final void main(java.lang.String[]); }
3. 删除本地仓库后重新构建
执行命令删除本地Maven仓库:
rm -rf ~/.m2/repository
重新构建后问题仍未解决。
4. 修改main方法实现
将main方法从:
runApplication<Spring302Application>(*args)
修改为:
SpringApplication.run(Spring302Application::class.java, *args)
但依然出现NoClassDefFoundError。
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("org.springframework.boot") version "3.0.2" id("io.spring.dependency-management") version "1.1.0" kotlin("jvm") version "1.7.22" kotlin("plugin.spring") version "1.7.22" } group = "com.kaede" version = "0.0.1-SNAPSHOT" java.sourceCompatibility = JavaVersion.VERSION_17 repositories { mavenCentral() } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") testImplementation("org.springframework.boot:spring-boot-starter-test") } tasks.withType<KotlinCompile> { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "17" } } tasks.withType<Test> { useJUnitPlatform() } tasks.withType<Jar> { manifest { attributes["Main-Class"] = "com.kaede.spring302.Spring302ApplicationKt" } }
你当前构建的是plain jar(仅包含项目自身class文件,不含任何依赖),所以运行时找不到Kotlin标准库等依赖类。Spring Boot Gradle插件提供的bootJar任务才能构建包含所有依赖的可执行fat jar,这是Spring Boot项目正确的可执行jar构建方式。
步骤1:移除自定义Jar任务配置
删除build.gradle.kts中这段自定义配置:
tasks.withType<Jar> { manifest { attributes["Main-Class"] = "com.kaede.spring302.Spring302ApplicationKt" } }
Spring Boot插件会自动识别Kotlin主类(包括生成的*Kt类),无需手动配置Manifest。
步骤2:构建可执行fat jar
执行Gradle的bootJar任务:
./gradlew bootJar
构建完成后,可执行jar会生成在build/libs/spring302-0.0.1-SNAPSHOT.jar(无-plain后缀)。
步骤3:测试运行Jar
运行生成的fat jar:
java -jar build/libs/spring302-0.0.1-SNAPSHOT.jar
此时应用应能正常启动,不会再出现NoClassDefFoundError。
步骤4:用于Docker构建
直接使用该fat jar编写Dockerfile,示例如下:
FROM openjdk:17-jdk-slim WORKDIR /app COPY build/libs/spring302-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT ["java", "-jar", "/app/app.jar"]
补充说明
plain jar是Gradle默认Jar任务的产物,仅适用于作为依赖被其他项目引用,无法单独运行。bootJar任务会自动打包所有依赖到jar中,并配置好启动类和类加载器,是Spring Boot项目的标准构建方式。
内容的提问来源于stack exchange,提问作者KAEDE

