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

Building FatJar for JavaFX with gradle and intellij, getting NoClassDefFOundError

我已经使用内置 Gradle 支持在 IntelliJ (2019.2) 中设置了一个 OpenJDK 12 项目。为了设计 GUI,我使用 JavaFX 12。我已经阅读 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

我已经尝试将我的主要 class 移动到一个不扩展应用程序的单独的应用程序,这就是导致上述错误的原因。在不移动我的 Main class 的情况下,我得到了一个不同的错误。

我的 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) }
    }
}

如果我有扩展应用程序的主要方法,那么我会收到一条错误消息,指出无法找到我的主要 class,即使我可以看到它存在于生成的 .jar 文件中。

我想做的就是生成一个文件,没有编程知识的朋友也可以运行。理想情况下,他们可以 运行 而无需先安装 Java 的文件。我知道应该可以这样做,但是 Gradle 对我来说是新的,所以我不确定这是否是导致所有这些头痛的原因。是否有更简单的部署方式?特别是考虑到这是一个独立项目?

编辑

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

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

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

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

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

因此,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 小节中进行了解释。

一旦你有了脂肪罐,你就可以运行它:

java -jar yourFatJar.jar

请注意,双击它不起作用,如详细说明,因此创建一个小批量会更方便。

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

您还可以使用 jpackageearly version 来创建和分发安装程序。