使用 gradle 和 intellij 为 JavaFX 构建 FatJar,得到 NoClassDefFOundError

Posted

技术标签:

【中文标题】使用 gradle 和 intellij 为 JavaFX 构建 FatJar,得到 NoClassDefFOundError【英文标题】:Building FatJar for JavaFX with gradle and intellij, getting NoClassDefFOundError 【发布时间】:2019-12-18 12:17:39 【问题描述】:

我使用内置的 Gradle 支持在 IntelliJ (2019.2) 中设置了一个 OpenJDK 12 项目。为了设计我使用 JavaFX 12 的 GUI。我已经多次阅读并阅读了setup 指南,我在 IDE 中运行程序没有问题,问题是当我尝试构建一个 .jar 文件进行分发时我遇到了问题。到目前为止,我还没有找到一个有效的解决方案,我已经搜索了很多,几乎把我的头发扯掉了。目前,当我尝试使用 java -jar "jarName".jar 运行我的 jar 文件时,出现以下错误:

Exception in thread "main" java.lang.NoClassDefFoundError: javafx/application/Application
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
        at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
        at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
        at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        at com.CAM.Starter.main(Starter.java:6)
Caused by: java.lang.ClassNotFoundException: javafx.application.Application
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 10 more

我尝试将我的主类移到一个不扩展应用程序的单独类中,这就是导致上述错误的原因。如果不移动我的 Main 类,我会得到一个不同的错误。

我的 build.gradle 目前看起来像这样:

plugins 
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.8'


group 'ClassicAddonManager'
version '0.2'
sourceCompatibility = 11

repositories 
    mavenCentral()


javafx 
    version = "12.0.2"
    modules = [ 'javafx.controls', 'javafx.fxml' ]


dependencies 
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile 'net.sourceforge.htmlunit:htmlunit:2.13'
    compile group: 'com.google.code.gson', name: 'gson', version: '2.7'
    compile group: 'net.lingala.zip4j', name: 'zip4j', version: '1.2.4'


jar 
    manifest 
        attributes 'Main-Class': 'com.CAM.Starter'
    
    from 
        configurations.compile.collect  it.isDirectory() ? it : zipTree(it) 
    

如果我的 main 方法扩展了 Application,那么我会收到一条错误消息,指出无法找到我的主类,即使我可以看到它存在于生成的 .jar 文件中。

我要做的只是生成一个文件,让不了解编程的朋友可以运行。理想情况下,他们无需先安装 Java 即可运行的文件。我知道应该可以做到这一点,但 Gradle 对我来说是新的,所以我不确定这是否是导致所有这些头痛的原因。是否有更简单的部署方式?尤其是考虑到这是一个单独的项目?

编辑

我已经尝试过指南的模块化部分。这样做我在尝试构建时有超过 100 个错误。它们都具有以下特点:

javafx.graphicsEmpty reads package org.xml.sax from both xml.apis and java.xml

【问题讨论】:

参见openjfx.io/openjfx-docs/#modular,使用 Gradle 的非模块化项目部分。 @JoséPereda 我已经阅读并尝试过了。这样做给我带来了另一个问题,即我一开始就无法生成 jar。收到一堆错误如:javafx.graphicsEmpty reads package org.xml.sax from both xml.apis and java.xml 当你在命令行上运行java -jar yourfatjar.jar,它会起作用吗? @JoséPereda 是的,效果很好。只有当我双击时。我试过弄乱注册表和默认程序,但没有。我也被这个问题困住了,如果我关闭 CMD 它将关闭 GUI。 关于双击,这个post可能会有帮助。 【参考方案1】:

如果你想使用 Gradle 而不是 shadow 插件来做一个 fat jar,通常你会这样做:

jar 
    manifest 
        attributes 'Main-Class': 'com.CAM.Starter'
    
    from 
        configurations.compile.collect  it.isDirectory() ? it : zipTree(it) 
    

但是,有一个重要的事实:compile 已被弃用,JavaFX 插件由default 使用implementation

因此,configuration.compile 可能为空,或者至少不包含 JavaFX 类。

解决方案是检查runtimeClasspath 配置,因为我们将在那里拥有所有类,我们可以制作fat jar:

jar 
    manifest 
        attributes 'Main-Class': 'com.CAM.Starter'
    
    from 
        configurations.runtimeClasspath.collect  it.isDirectory() ? it : zipTree(it) 
    

这在 OpenJFX 文档https://openjfx.io/openjfx-docs/#modular,非模块化项目部分,gradle 小节中进行了解释。

一旦你有了你的 fat jar,你可以运行它:

java -jar yourFatJar.jar

注意双击是不行的,详细解释here,所以可以方便的改成小批量。

更好的解决方案是做一个模块化项目并使用jlink 创建一个运行时映像(它还包括一个启动器脚本)。您可以将此映像分发给其他甚至没有安装 JDK 的用户。使用 gradle,您可以只包含 'org.beryx.jlink' 插件,就像在这个 sample 中一样。

您还可以使用early version 的jpackage 创建分发安装程序。

【讨论】:

以上是关于使用 gradle 和 intellij 为 JavaFX 构建 FatJar,得到 NoClassDefFOundError的主要内容,如果未能解决你的问题,请参考以下文章

使用 gradle 和 intellij 为 JavaFX 构建 FatJar,得到 NoClassDefFOundError

在IntelliJ中创建gradle项目的问题

无法使用 AutoValue 和 IntelliJ 解析符号

使用 Gradle build 在 IntelliJ IDEA 中获取 Gradle 依赖项

在 IntelliJ 2016 中使用 Gradle 安装 Guava

在 Intellij 中将 Java 项目转换为 Gradle 项目