【发布时间】:2020-01-21 08:25:51
【问题描述】:
我们正在使用 gradle 使用 Adopt JDK 11 和 IntelliJ 开发 JavaFX 11 应用程序。最后,我们需要一个适用于 windows 的 EXE 文件(该应用程序仅适用于 windows)。在第一次尝试中,我们还在 gradle 中使用了 launch4J,由于以下问题,我们也可以使用 BAT 文件,我们可以将其迁移到 exe 文件。
所以我们的主要目标和问题是,我们如何创建可执行的 jar 文件。
我们采取了几种不同的方法,但我们完全迷失了。
FAT JAR
我们为测试目的制作了一个独立的应用程序:
package de.test;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
VBox vBox = new VBox();
Button button = new Button("Klick");
TextField textfield = new TextField();
TextArea area = new TextArea();
area.setMinHeight(300);
button.setOnAction(event -> area.setText(area.getText() + " -- Klick Version 1.0.8"));
vBox.getChildren().addAll(button, textfield,area);
primaryStage.setScene(new Scene(vBox));
primaryStage.setTitle("Test");
primaryStage.show();
}
}
FAT JAR
我们的第一个尝试是使用 gradle 的 javafx 插件创建一个带有或不带有 modules-info.java 文件的 FAT-JAR。这是我们的 gradle 文件
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
}
group 'de.test'
version '1.0.8'
sourceCompatibility = 11
javafx {
modules = ['javafx.controls']
}
mainClassName = 'de.test.Main'
jar {
manifest {
attributes 'Main-Class': mainClassName
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
repositories {
mavenLocal()
mavenCentral()
}
运行 gradle clean jar 后,我们尝试使用以下命令执行 jar java -jar ApplicationTest-1.0.8.jar 结果:
错误:无法找到或加载主类 de.test.Main 原因: java.lang.NoClassDefFoundError: javafx/application/Application
带有依赖项和 module-info.java 的 FAT JAR
然后我们尝试在 gradle 文件中像这样添加 JavaFX 的依赖项
compile group: 'org.jboss.resteasy', name: 'resteasy-client', version: '4.3.0.Final'
compile "org.openjfx:javafx-graphics:11.0.2:win"
compile "org.openjfx:javafx-base:11.0.2:win"
compile "org.openjfx:javafx-controls:11.0.2:win"
compile "org.openjfx:javafx-fxml:11.0.2:win"
compile "org.openjfx:javafx-graphics:11.0.2:win"
这是我们添加到包 de.test 中的 module-info.java 描述符:
module ApplicationTest.main {
requires javafx.controls;
exports de.test;
}
当调用 gradle installDist 我们使用了这个命令
cd build\install\ApplicationTest\lib java --add-modules “javafx.controls”——模块路径。 -jar ApplicationTest-1.0.8.jar
至少应用程序此时已启动,但布局已被以下信息消息破坏:
九月。 2019 年 2 月 20 日 8:32:55 VORM。 com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged 信息:无法加载样式表: com/sun/javafx/scene/control/skin/modena/modena.css 2019 年 9 月 20 日 8:32:55 VORM。 com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged 信息:无法加载样式表: com/sun/javafx/scene/control/skin/modena/modena.css
顺便说一句:使用命令 gradle clean run
时 IntelliJ 一直在工作JLink 与 module-info.java
下一个方法是使用 jlink 和以下 modules-info.java
module ApplicationTest.main {
requires javafx.controls;
requires javafx.graphics;
exports de.test;
}
我们需要像下面这样扩展 gradle 文件
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
id 'org.beryx.jlink' version '2.10.4'
}
group 'de.test'
version '1.0.8'
sourceCompatibility = 11
mainClassName = 'de.test.Main'
javafx {
version = 11
modules = [ 'javafx.controls']
}
jlink {
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
}
repositories {
mavenLocal()
mavenCentral()
}
当使用 gradle jlink 并执行 cd build\image\bin\ApplicationTest.bat 文件时,应用程序会启动 WITH 布局。
此时我们哪里有种幸福。现在我们需要为我们的应用程序添加一些依赖项。
使用以下依赖项效果很好
dependencies {
compile "commons-io:commons-io:2.6"
compile "org.apache.logging.log4j:log4j-api:2.11.2"
compile "dom4j:dom4j:1.6.1"
compile "commons-lang:commons-lang:2.6"
compile "axis:axis:1.4"
compile "jaxen:jaxen:1.1.6"
compile "net.java.dev.jna:platform:3.5.2"
compile "org.apache.poi:poi:4.1.0"
compile "org.apache.poi:poi-scratchpad:4.1.0"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4"
}
但是,一旦我添加了其他依赖项,我就会收到不同类型的错误消息。我单独尝试了每个依赖项以隔离错误消息。
org.jboss.resteasy - resteasy-client - 3.7.0.Final
Cannot derive uses clause from service loader invocation in: javax/ws/rs/client/FactoryFinder.find().
Cannot derive uses clause from service loader invocation in: javax/ws/rs/ext/FactoryFinder.find().
Cannot derive uses clause from service loader invocation in: javax/ws/rs/sse/FactoryFinder.find().
Cannot derive uses clause from service loader invocation in: javax/xml/bind/ServiceLoaderUtil.firstByServiceLoader().
ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:154: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
provides javax.ws.rs.ext.Providers with org.jboss.resteasy.plugins.interceptors.CacheControlFeature,
^
ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:155: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
org.jboss.resteasy.plugins.interceptors.encoding.ClientContentEncodingAnnotationFeature,
^
ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:156: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
org.jboss.resteasy.plugins.interceptors.encoding.MessageSanitizerContainerResponseFilter,
^
...
^
25 errors
依赖:org.apache.logging.log4j:log4j-core:2.11.2
package org.apache.logging.log4j.spi is not visible
(package org.apache.logging.log4j.spi is declared in module org.apache.logging.log4j, but module de.test.merged.module does not read it)
package org.apache.logging.log4j.message is not visible
(package org.apache.logging.log4j.message is declared in module org.apache.logging.log4j, but module de.test.merged.module does not read it)
依赖:org.jboss.resteasy:resteasy-multipart-provider:3.7.0.Final
module not found: java.activation
依赖:org.jboss.resteasy:resteasy-jackson2-provider:3.7.0.Final
package javax.ws.rs.ext does not exist
package javax.ws.rs.ext does not exist
package javax.ws.rs.ext does not exist
结论
我们现在没有任何进展。它可能与 modules-info.java 有关,但有关此主题的说明和文档非常复杂且通常非常详细。我们只是想要一个可以在 IntelliJ 之外启动的应用程序。
我们对所有解决方案持开放态度,只要它们有效。我们也不需要最先进的解决方案,但我们最终可以通过某种方式获得 EXE(也可以借助外部工具)。我们的应用程序不必是模块化的,但我们既没有得到也没有解决它。
编辑 1) 第二个主类的 FAT Jar
就像在this aricle 中一样,我们添加了第二个主java 类而不扩展Application 并将其放入gradle 文件中
package de.test;
public class Main {
public static void main(String[] args) {
MainFX.main(args);
}
}
另一个类看起来像这样:
package de.test;
import ...;
public class MainFX extends Application {
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
...
}
}
gradle 文件如下所示:
plugins {
id 'java'
id 'application'
}
group 'de.test'
version '1.0.8'
sourceCompatibility = 11
mainClassName = 'de.test.MainFX'
jar {
manifest {
attributes 'Main-Class': mainClassName
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile "commons-io:commons-io:2.6"
compile "org.apache.logging.log4j:log4j-api:2.11.2"
compile "dom4j:dom4j:1.6.1"
compile "commons-lang:commons-lang:2.6"
compile "axis:axis:1.4"
compile "jaxen:jaxen:1.1.6"
compile "net.java.dev.jna:platform:3.5.2"
compile "org.apache.poi:poi:4.1.0"
compile "org.apache.poi:poi-scratchpad:4.1.0"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4"
compile "org.jboss.resteasy:resteasy-multipart-provider:3.7.0.Final"
compile "org.jboss.resteasy:resteasy-jackson2-provider:3.7.0.Final"
compile "org.apache.logging.log4j:log4j-core:2.11.2"
compile "org.jboss.resteasy:resteasy-client:3.7.0.Final"
compile "org.openjfx:javafx-graphics:11.0.2:win"
compile "org.openjfx:javafx-base:11.0.2:win"
compile "org.openjfx:javafx-controls:11.0.2:win"
compile "org.openjfx:javafx-fxml:11.0.2:win"
compile "org.openjfx:javafx-graphics:11.0.2:win"
}
结果真奇怪java -jar ApplicationTest.jar返回
错误:无法找到或加载主类 de.test.MainFX 原因: java.lang.ClassNotFoundException: de.test.MainFX
我们仔细检查了 jar 文件,并且类文件位于正确的位置(de\test\Main.class 和 de\test\MainFX.class)
编辑 2) FAT Jar 与第二个主类
基于this article,我们再次添加了 javafx 插件,移除了 javafx 依赖项,并在清单中定义了 Launcher-Main,并将常规 FXML 邮件定义为 mainClassName:
...
mainClassName = 'de.test.MainFX'
javafx {
version = 11
modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web']
}
jar {
manifest {
attributes 'Main-Class': 'de.test.MainLauncher'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
结果是
> 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:174)
> 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 de.test.MainLauncher.main(MainLauncher.java:7) 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
所以我们再次添加 javafx 的依赖项,结果如下:
Graphics Device initialization failed for : d3d, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
at com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
at com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:222)
at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:260)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
... 1 more
Exception in thread "main" java.lang.RuntimeException: No toolkit found
at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:272)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:834)
【问题讨论】:
-
对于一个胖 JAR,你的主类不能扩展
Application。 -
亲爱的 Slaw,我们昨天已经在第二个主要课程上尝试过,但没有运气。我们只是忘了提及这一点。我在底部编辑并添加了结果。今晚我会检查其他链接。