JavaFX 桌面应用程序无法在特定机器上启动(可在其他机器上运行)

JavaFX desktop app won't start in a specific machine (works in others)

我有一个 java 使用 Swing 的桌面应用程序,我使用 SceneBuilder 在 JavaFX 中重写了它。

Swing 应用程序在所有用户机器(35 台机器)上运行良好。在用 JavaFX 重写应用程序后,在一台特定的机器上应用程序无法启动并给出以下错误消息。

我无法弄清楚这个问题。如果有人可以提供任何帮助,我将不胜感激。 谢谢你。

Exception in Application start method
java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication(Unknown Source)
        at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: com/sun/javafx/css/converters/PaintConverter
        at com.jfoenix.controls.JFXPasswordField$StyleableProperties.<clinit>(JFXPasswordField.java:205)
        at com.jfoenix.controls.JFXPasswordField.<init>(JFXPasswordField.java:156)
        at idehmis.controller.LoginController.<init>(LoginController.java:51)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source)
        at java.base/java.lang.Class.newInstance(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processAttribute(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader$Element.processStartElement(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processStartElement(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.processStartElement(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml/javafx.fxml.FXMLLoader.load(Unknown Source)
        at idehmis.IDEHMIS.start(IDEHMIS.java:30)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(Unknown Source)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(Unknown Source)
        at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop(Unknown Source)
        ... 1 more
Caused by: java.lang.ClassNotFoundException: com.sun.javafx.css.converters.PaintConverter
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
        at java.base/java.lang.ClassLoader.loadClass(Unknown Source)

看起来 JVM 发现很难在运行时定位 com.sun.javafx.css.converters.PaintConverter,这会导致 ClassNotFoundException 异常错误,进而导致 JFoenix 库抛出 java.lang.NoClassDefFoundError 含义在 class 路径上找不到 class。这表明我们正在尝试加载 class 定义,而 class 在 class 路径 上不存在 所以首先要确保你有 正确的 java 版本,即 Java 8 以支持 JFoenix,他们确保JFoenix 库位于您的 classpath.

堆栈跟踪,因为它在每个 fully-qualified class 名称(例如 java.lang.ClassLoader)前面加上模块名称(例如 java.base),表明机器是尝试 运行 应用程序使用 Java 9.

CSS 转换器 classes,例如 PaintConverter,是从私有 API 提升而来的,它们位于 Java 8 及更早版本, to public API in Java 9. 因此 class com.sun.javafx.css.converters.PaintConverter 不再存在,并已被 javafx.css.converter.PaintConverter 取代。因此,当 运行 在 Java 9 中使用你的应用程序时,你会得到一个 ClassNotFoundException.

这里的底线是,使用任何 API 而不是 public,或者使用这样做的第三方库,如果用户更新他们的 JVM,可能会使您的应用程序失败。此问题的可能解决方案是:

  • 避免使用私有 API 或使用私有 API
  • 的库
  • 创建和维护适用于特定 JVM 版本的不同代码版本。对于您正在使用的 third-party JFoenix 库,Java 9 有一个单独的版本,因此您可以创建一个使用该版本的应用程序的 Java 9 版本图书馆。请注意,这不仅会给开发人员带来额外的负担,而且还可能给最终用户带来问题,他们将被迫在更新 JVM 的同时更新他们使用的应用程序版本。
  • 为您的应用程序创建一个 "Self-Contained Application Bundle"。这将特定的 JVM 与您的代码捆绑在一起,因此您基本上可以控制用户用于 运行 您的应用程序的 JVM。这样做的缺点是您需要为要支持的每个平台(Windows 32 位、Windows 64 位、Mac、Linux)创建不同的包,并且应用程序的大小将大幅增加(包括 JVM)。

我通常会在可能的情况下推荐第一个选项,在不可能的情况下推荐第三个选项。