Spring Boot Kotlin 项目用gradle构建运行时报错 “NoSuchMethodException” 的问题排查与解决

在开发基于 Spring Boot 和 Kotlin 的项目时,构建成功但运行时出现以下错误:

Exception in thread "main" java.lang.NoSuchMethodException: site.achun.biz.share.ShareApplication.main([Ljava.lang.String;)
        at java.base/java.lang.Class.getDeclaredMethod(Class.java:2675)
        ...

问题背景

报错场景

  • 使用 Kotlin 和 Spring Boot 构建 REST 服务。

  • 构建工具为 Gradle。

  • 项目结构清晰,顶层入口定义如下:

    @SpringBootApplication
    class ShareApplication
    
    fun main(args: Array<String>) {
        runApplication<ShareApplication>(*args)
    }
    

关键日志

运行 JAR 包时,报以下错误:

Exception in thread "main" java.lang.NoSuchMethodException: site.achun.biz.share.ShareApplication.main([Ljava.lang.String;)

排查发现

通过 javap 检查生成的类文件,发现 main 方法并未生成在 ShareApplication.class 中,而是被编译到了 ShareApplicationKt.class


问题分析

在 Kotlin 中,顶层函数默认会被编译到独立的类中,并添加 Kt 后缀。例如,main 方法会被放入 ShareApplicationKt.class 中。但 Spring Boot 默认查找入口方法时,只会寻找 Start-Class 指向的类中的 main 方法。

具体来说:

  • Start-Class 的值:定义在 build.gradle.ktsapplication.mainClass 中,默认为 ShareApplication
  • 实际入口方法所在的类ShareApplicationKt

这导致 Spring Boot 无法找到正确的入口方法,抛出 NoSuchMethodException


解决方法

针对该问题,有两种常用解决方案:

方案 1:将 main 方法移入类中

为了兼容 Java 的传统规范,可以将 main 方法移入 ShareApplication 类中。修改后的代码如下:

@SpringBootApplication
class ShareApplication {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            runApplication<ShareApplication>(*args)
        }
    }
}

关键点

  • 使用 companion object 声明静态上下文。
  • 添加 @JvmStatic 注解,确保生成静态的 main 方法。

完成后,重新构建并运行:

./gradlew clean build
java -jar build/libs/achun-biz-share-1.0-SNAPSHOT.jar

方案 2:修改 Start-Class 指向 ShareApplicationKt

如果希望保持 Kotlin 的默认行为(顶层函数保留在 Kt 后缀类中),只需修改 Gradle 的 mainClass 配置:

build.gradle.kts 中,更新 application 插件的 mainClass 配置:

application {
    mainClass.set("site.achun.biz.share.ShareApplicationKt")
}

然后重新构建:

./gradlew clean bootJar
java -jar build/libs/achun-biz-share-1.0-SNAPSHOT.jar

效果

  • Spring Boot 的 Start-Class 会正确指向 ShareApplicationKt,找到入口方法。

验证

检查 META-INF/MANIFEST.MF

无论采用哪种方案,重新构建后,确保 JAR 包的 META-INF/MANIFEST.MF 文件包含以下内容:

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: site.achun.biz.share.ShareApplicationKt

验证入口类

检查生成的类文件,确认 main 方法位置是否正确:

javap -c build/classes/kotlin/main/site/achun/biz/share/ShareApplicationKt.class

最终选择

我们最终采用了 方案 2,因为它简单且更符合 Kotlin 的特性,无需改变代码逻辑。


总结

经验教训

  • Kotlin 项目中,顶层函数默认会被编译到独立的 Kt 类中。
  • Spring Boot 默认只在 Start-Class 指向的类中查找 main 方法。

推荐方法

  • 如果追求与 Java 的兼容性,推荐使用 方案 1
  • 如果希望充分利用 Kotlin 特性,可以选择 方案 2,只需在 Gradle 中调整 mainClass 配置。

常用排查命令

  • 查看 JAR 包内容:
    jar -tf build/libs/<your-jar>.jar
    
  • 检查 META-INF/MANIFEST.MF
    unzip -p build/libs/<your-jar>.jar META-INF/MANIFEST.MF
    
  • 查看生成的类文件:
    javap -c build/classes/kotlin/main/<your-class>.class
    

在这里插入图片描述

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐