java—如何使用gluonhq客户机、本机映像和graalvm解决已编译javafx项目中的fxml加载异常?

9nvpjoqh  于 2021-07-08  发布在  Java
关注(0)|答案(4)|浏览(798)

这是这个问题的后续问题。
我试图将javafx项目编译成一个本地映像,这样它就可以在本地运行,而不需要安装java。javafx和反射的问题已经通过gluonhq客户机插件解决了,因此编译现在是成功的。
我已经设法获得了一个简单的javafx项目(intellij在创建javafx项目时生成的示例)来使用glion客户机maven插件进行编译。但是,在命令行上运行本机映像时,会出现javafx fxml加载异常:

Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
        at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
        at java.lang.Thread.run(Thread.java:834)
        at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:518)
        at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
Caused by: javafx.fxml.LoadException: 
sample.fxml:8

        at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2629)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2607)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2470)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3241)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3198)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3167)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3140)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3117)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3110)
        at sample.Main.start(Main.java:13)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
        at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
        at java.security.AccessController.doPrivileged(AccessController.java:101)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
        at com.oracle.svm.jni.JNIJavaCallWrappers.jniInvoke_VA_LIST:Ljava_lang_Runnable_2_0002erun_00028_00029V(JNIJavaCallWrappers.java:0)
        at com.sun.glass.ui.gtk.GtkApplication._runLoop(GtkApplication.java)
        at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
        ... 3 more
Caused by: com.sun.javafx.fxml.PropertyNotFoundException: Property "alignment" does not exist or is read-only.
        at javafx.fxml.FXMLLoader$Element.processValue(FXMLLoader.java:355)
        at javafx.fxml.FXMLLoader$Element.processPropertyAttribute(FXMLLoader.java:332)
        at javafx.fxml.FXMLLoader$Element.processInstancePropertyAttributes(FXMLLoader.java:242)
        at javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:775)
        at javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2842)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2561)
        ... 20 more

只有通过更改sample.fxml才能使本机映像正常工作:

<?import javafx.scene.layout.GridPane?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
</GridPane>

为此(删除alignment、hgap和vgap属性):

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml">
</GridPane>

然后重新编译。然后编译的二进制文件按预期运行。
对于pom.xml中的glion插件,反射配置如下:

<plugin>
    <groupId>com.gluonhq</groupId>
    <artifactId>client-maven-plugin</artifactId>
    <version>0.1.30</version>
    <configuration>
        <mainClass>sample.NewMain</mainClass>
        <reflectionList>
            <list>sample.Main</list>
            <list>sample.NewMain</list>
            <list>sample.Controller</list>
            <list>javafx.fxml.FXMLLoader</list>
        </reflectionList>
    </configuration>
</plugin>

一旦使用反射配置编译了较大的javafx项目,这些fxml加载异常就相同了。例外总是说 Caused by: com.sun.javafx.fxml.PropertyNotFoundException: Property [X] does not exist or is read-only. 这两个项目在jvm上运行良好,没有抛出异常。我的ide无法检测到代码错误。

hxzsmxv2

hxzsmxv21#

我将进一步扩展@mipa的答案。
您可能知道,fxml完全是关于反射的:我们有一个(f)xml文件和一个解析器( FXMLLoader ),找到类( GridPane ),和属性名称( alignment )解析为方法名的( setAlignment(Pos) 以及 getAlignment() )在分析那个文件的时候。
默认情况下,客户端插件为您提供 reflectionConfig.json 包含您可能在fxml文件中使用的大多数javafx类和方法的文件。
正如您在这里看到的,这个文件是在运行时生成的 mvn client:compile (或 mvn client:link ),可在 target/client/$arch-$os/gvm/reflectionconfig-$arch-$os.json (使用目标体系结构和操作系统名称)。
现在,它包含大约290个类(java和javafx),其中包含字段和方法。
如果你检查一下,你会发现 GridPane 班级:

,
  {
    "name" : "javafx.scene.layout.GridPane",
    "methods":[
      {"name":"<init>","parameterTypes":[] },
      {"name":"setRowIndex","parameterTypes":["javafx.scene.Node","java.lang.Integer"] },
      {"name":"getRowIndex","parameterTypes":["javafx.scene.Node"] },
      {"name":"setColumnIndex","parameterTypes":["javafx.scene.Node","java.lang.Integer"] },
      {"name":"getColumnIndex","parameterTypes":["javafx.scene.Node"] },
      {"name":"setColumnSpan","parameterTypes":["javafx.scene.Node","java.lang.Integer"] },
      {"name":"getColumnSpan","parameterTypes":["javafx.scene.Node"] },
      {"name":"setRowSpan","parameterTypes":["javafx.scene.Node","java.lang.Integer"] },
      {"name":"getRowSpan","parameterTypes":["javafx.scene.Node"] },
      {"name":"getRowConstraints","parameterTypes":[] },
      {"name":"getColumnConstraints","parameterTypes":[] },
      {"name":"setHgrow","parameterTypes":["javafx.scene.Node","javafx.scene.layout.Priority"] },
      {"name":"getHgrow","parameterTypes":["javafx.scene.Node"] },
      {"name":"setVgrow","parameterTypes":["javafx.scene.Node","javafx.scene.layout.Priority"] },
      {"name":"getVgrow","parameterTypes":["javafx.scene.Node"] },
      {"name":"setMargin","parameterTypes":["javafx.scene.Node","javafx.geometry.Insets"] },
      {"name":"getMargin","parameterTypes":["javafx.scene.Node"] }
    ]
  }
,

正如您所注意到的,它包含构造函数和中的所有静态方法 GridPane ,因此这适用于客户端插件:

<GridPane>
    <Label text="a label" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
</GridPane>

然而 alignment 方法不包括在内,这就是为什么您的fxml失败的原因。
有两种可能的解决方案:
1.配置文件
在“配置文件”部分之后,可以将自己的文件添加到项目中,并添加缺少的方法:
创建文件 reflectionconfig.json 低于 src/main/resources/META-INF/substrate/config/ 添加缺少的方法:

[
  {
    "name" : "javafx.scene.layout.GridPane",
    "methods":[
      {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] },
      {"name":"getAlignment","parameterTypes":[] },
      {"name":"setHgap","parameterTypes":["double"] },
      {"name":"getHgap","parameterTypes":[] },
      {"name":"setVgap","parameterTypes":["double"] },
      {"name":"getVgap","parameterTypes":[] }
    ]
  }
]

再跑一次 mvn client:build client:run ,这次应该可以了。
如果你再检查一下 target/client/$arch-$os/gvm/reflectionconfig-$arch-$os.json 文件,您将看到json文件的内容已经包含在末尾,现在所有的 GridPane 方法可用于反射。
2.反射列表
或者,您可以简单地将整个类添加到reflectionlist:

<reflectionList>
    <list>javafx.scene.layout.GridPane</list>
</reflectionList>

运行之后,检查json文件,您将看到:

{
    "name" : "javafx.scene.layout.GridPane",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredFields" : true,
    "allPublicFields" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  }

与选项1的区别在于,现在您告诉graalvm将该类的所有声明的和公共的构造函数、字段和方法添加到它的反射列表中,不管它们是否被使用,这可能会对编译时间和内存占用产生(很小)影响。理想情况下,上述方案1优于方案2。
只提供所需的类/方法将是最好的,但是正如@mipa指出的,这将需要一些能够发现哪些是这些类/方法的工具。同时,您必须运行一些迭代,以确定默认json文件是否包含fxml文件中使用的所有类/方法,并将缺少的类/方法添加到反射文件中(或者将类名添加到反射列表中)。

a7qyws3x

a7qyws3x2#

我只想给乔斯加一个更具体的案子é'也许他也能澄清这一点。
另一个恼人的问题是,有时仅仅将要加载的类放入反射列表是不够的。e、 例如,如果要加载progressbar并将该类放入反射列表中,仍然会出现以下错误: ProgressBar Property "progress" does not exist or is read-only . 原因是progressbar的超类中定义了属性“progress”,因此您还必须将progressindicator添加到列表中。
为什么会这样?它能以某种方式预防吗?

omhiaaxx

omhiaaxx3#

您必须将更多通过fxml加载的类添加到反射列表中。e、 我还得补充一句 javafx.geometry.HPos 以及 javafx.geometry.VPos . 这是复杂的事实,这是不一致的。一些类在默认情况下已经包含了,而其他类则没有。你需要一些实验。有时,如果已经在类中定义了属性,我甚至不得不指定类的父级。出于我自己的目的,我编写了一个小工具来简化这个过程:https://github.com/mipastgt/jfxtoolsanddemos#fxml-检查器

ilmyapht

ilmyapht4#

这是如此令人沮丧,但最后我得到了一切的权利,这里有一些建议。如果您有fxml文件并且该文件加载在controller中,只需在reflectionlist中添加控制器就可以了。resourcereflection.json不包含javafx类的所有属性,请检查是否在json中添加了所有属性,如果不只是在reflectionlist中添加fx类,那么add javafx.scene.layout.gridpane将添加gridpane的所有属性。检查fxml中的import类,其中一些可能不会添加到json文件中,只要找到它,就添加到reflectionlist,例如,如果在fxml文件中有radiobutton、combobox,则在生成的json文件中找不到它们。所以1。将生成的resourcerelection.json复制并添加到meta-inf.substrate.config目录中。2.将所有类添加到reflectionlist,不需要添加用于加载fxml文件的视图。3.将e.printstacktrace()添加到您的fxml加载程序以检查异常中发生的情况。4.mvn清洁client:build client:运行。
快乐的编码!干得好胶子团队。

相关问题