从命令行运行时,混合Java和Kotlin代码导致ClassNotFoundException

6
我在一个演示项目中混合了JavaKotlin代码,我想从命令行运行该项目。对于只有Java的情况,我可以使用java -jar foo.jar运行程序,但是当我使用任何Kotlin代码的类时,就会出现生成的错误消息:java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
我尝试了不同的解决方案并检查了jar文件。它包含两个类,但我想缺少了kotlin-runtime
这里是build.gradle文件:
plugins {
    id 'java-library'
    id 'org.jetbrains.kotlin.jvm'
}

sourceSets {
    main.java.srcDirs += 'src/main/kotlin/'
    test.java.srcDirs += 'src/test/kotlin/'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    testImplementation 'junit:junit:4.12'
}

jar {
    manifest {
        attributes 'Main-Class': 'com.mallaudin.App'
    }
    from {
        configurations.compile.collect {
            it.isDirectory()? it: zipTree(it)
        }
    }
}

生成的 jar 文件的内容。
.
├── com
│   └── mallaudin
│       ├── App.class
│       └── User.class
└── META-INF
    ├── basics.kotlin_module
    └── MANIFEST.MF

MANIFEST.MF的内容

Manifest-Version: 1.0
Main-Class: com.mallaudin.App

当我从命令行运行jar时,我收到的异常

allaudin@geek ~/Desktop/KotlinLab (master) $ java -jar basics/build/libs/basics.jar 
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
    at com.mallaudin.User.<init>(User.kt)
    at com.mallaudin.App.main(App.java:5)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
    ... 2 more

1
不要使用图像。复制并粘贴问题中的文本。 - SeverityOne
请问您能否发布 build.gradle 文件中的依赖项部分?我猜想 Kotlin 运行时并未被定义为编译时依赖项。 - Erik Pragt
@ErikPragt 请检查更新后的问题。 - mallaudin
@ErikPragt 我也这么认为,但我已经添加了依赖项。我不知道发生了什么事。 - mallaudin
这可能是一个相当清晰的说明:https://dev59.com/ZlcP5IYBdhLWcg3wYo_i - Erik Pragt
2个回答

3
问题出在你构建fat jar的方式上。虽然你使用了更新且推荐的“implementation”范围来添加依赖项,但你只将“compile”配置的内容添加到了fat jar中。
你应该使用“runtimeClasspath”替换在fat jar中使用的配置,这是完整的运行时组件集合。
即:
jar {
  manifest {
    attributes 'Main-Class': 'com.mallaudin.App'
  }
  from {
    configurations.runtimeClasspath.collect {
      it.isDirectory()? it: zipTree(it)
    }
  }
}  

请查看文档,了解Java项目的不同依赖配置。

0
所以,我找到了一个解决方案。在`build.gradle`文件中,我用`compile`替换了`implementation`配置,并且现在它可以工作了。implementation应该也可以工作,但实际上并没有。现在的`jar`文件看起来是这样的:
.
├── com
├── kotlin
├── META-INF
└── org

1
嗨,我很高兴现在它可以工作了,但这就是我在之前的评论中说过的;-) - Erik Pragt
1
但是为什么它在使用“implementation”时不起作用呢?你有任何想法吗? - mallaudin
我相信它在实现时也会起作用,但是你需要更改这一行:configurations.compile,以便还包括实现:configurations.implementation。目前,您只包括编译范围,因为那是“旧”做事情的方式。 - Erik Pragt
不允许使用 configurations.implementation。我已经检查过了。 - mallaudin
我也遇到了同样的问题。从“implementation”切换到“api”也没有帮助。 - Maroun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接