升级Java8到11后,如何让GraalVM原生镜像使用外部JavaFX SDK?
问题描述
将Java 8项目升级到Java 11后,使用GraalVM构建原生镜像时遇到以下问题:
- 编译链接成功,但运行时出现警告
JavaFx configuration: classes were loaded from unnamed module - IDE中通过VM参数
--module-path javafx-sdk-19/lib --add-modules javafx.fxml,javafx.controls,javafx.graphics可解决该警告,但添加到GluonFX插件的runtimeArgs后,原生镜像仍无法正常运行 - 同时抛出Resteasy相关初始化错误:
Invalid bundle interface org.jboss.resteasy.resteasy_jaxrs.i18n.Messages (implementation not found)
相关Maven插件配置:
addjars-maven-plugin配置
<plugin> <groupId>com.googlecode.addjars-maven-plugin</groupId> <artifactId>addjars-maven-plugin</artifactId> <version>1.0.2</version> <executions> <execution> <goals> <goal>add-jars</goal> </goals> <configuration> <resources> <resource> <directory>${basedir}/libs</directory> </resource> </resources> </configuration> </execution> </executions> </plugin>
GluonFX插件配置
<plugin> <groupId>com.gluonhq</groupId> <artifactId>gluonfx-maven-plugin</artifactId> <version>1.0.15</version> <configuration> <mainClass>com.proj.main.Main</mainClass> <reflectionList>com.proj.main.PaxUtils</reflectionList> <reflectionList>com.proj.JsonUtil</reflectionList> <nativeImageArgs> <arg>+EagerJVMCI</arg> <arg>-Dgraal.PrintConfiguration=info</arg> </nativeImageArgs> </configuration> </plugin>
错误日志
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @7e0babb1' Exception in Application start method Exception in thread "main" java.lang.RuntimeException: Exception in Application start method at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:901) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196) at java.lang.Thread.run(Thread.java:829) at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704) at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202) Caused by: java.lang.ExceptionInInitializerError at org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:164) at org.jboss.resteasy.spi.ResteasyProviderFactory.createProviderInstance(ResteasyProviderFactory.java:2835) at org.jboss.resteasy.spi.ResteasyProviderFactory.addMessageBodyReader(ResteasyProviderFactory.java:1068) at org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:1841) at org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:1769) at org.jboss.resteasy.plugins.providers.RegisterBuiltin.registerProviders(RegisterBuiltin.java:148) at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:54) at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:40) at org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.getProviderFactory(ResteasyClientBuilder.java:456) at org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.buildOld(ResteasyClientBuilder.java:464) at org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.build(ResteasyClientBuilder.java:496) at org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.build(ResteasyClientBuilder.java:50) at javax.ws.rs.client.ClientBuilder.newClient(ClientBuilder.java:114) at com.xylo.client.xpos.service.XWebServiceClient.init(XWebServiceClient.java:41) at com.xylo.client.xpos.service.XWebServiceClient.<init>(XWebServiceClient.java:31) at com.xylo.client.xpos.POSSettings.fillSettings(POSSettings.java:181) at com.xylo.client.xpos.main.Main.start(Main.java:45) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:847) at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:484) at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457) at java.security.AccessController.executePrivileged(AccessController.java:169) at java.security.AccessController.doPrivileged(AccessController.java:91) at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96) at com.oracle.svm.jni.JNIJavaCallWrappers.jniInvoke_VA_LIST_Runnable_run_16403f8d32adb631126daa893e5e80085c5d6325(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:316) ... 3 more Caused by: java.lang.IllegalArgumentException: Invalid bundle interface org.jboss.resteasy.resteasy_jaxrs.i18n.Messages (implementation not found) at org.jboss.logging.Messages.doGetBundle(Messages.java:92) at org.jboss.logging.Messages.getBundle(Messages.java:59) at org.jboss.logging.Messages.getBundle(Messages.java:46) at org.jboss.resteasy.resteasy_jaxrs.i18n.Messages.<clinit>(Messages.java:31) ... 30 more
解决方案
1. 正确配置JavaFX模块到原生镜像构建参数
IDE中的VM参数是给JVM运行时用的,原生镜像构建时需要将这些参数传递给GraalVM的native-image工具,因此要添加到GluonFX插件的nativeImageArgs中,而不是runtimeArgs:
<plugin> <groupId>com.gluonhq</groupId> <artifactId>gluonfx-maven-plugin</artifactId> <version>1.0.15</version> <configuration> <mainClass>com.proj.main.Main</mainClass> <reflectionList>com.proj.main.PaxUtils</reflectionList> <reflectionList>com.proj.JsonUtil</reflectionList> <nativeImageArgs> <arg>+EagerJVMCI</arg> <arg>-Dgraal.PrintConfiguration=info</arg> <!-- 替换为你的JavaFX SDK实际路径 --> <arg>--module-path=${basedir}/javafx-sdk-19/lib</arg> <arg>--add-modules=javafx.fxml,javafx.controls,javafx.graphics</arg> <!-- 可选:添加模块导出,解决可能的内部API访问问题 --> <arg>--add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED</arg> </nativeImageArgs> </configuration> </plugin>
2. 替换addjars为Maven依赖管理(推荐)
addjars-maven-plugin会将JavaFX的模块Jar当作普通Jar处理,导致原生镜像无法识别模块结构。建议直接通过Maven依赖引入JavaFX,GluonFX插件会自动处理模块配置:
<dependencies> <!-- 根据你的操作系统选择classifier:linux/win/mac --> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>19</version> <classifier>linux</classifier> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> <version>19</version> <classifier>linux</classifier> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-graphics</artifactId> <version>19</version> <classifier>linux</classifier> </dependency> </dependencies>
之后移除addjars-maven-plugin中对JavaFX Jar的引用,避免冲突。
3. 修复Resteasy的反射与资源缺失问题
错误日志显示Resteasy的国际化消息类无法找到实现,这是因为GraalVM原生镜像不会自动包含反射和资源文件,需要手动配置:
方式一:添加反射配置到GluonFX插件
<plugin> <groupId>com.gluonhq</groupId> <artifactId>gluonfx-maven-plugin</artifactId> <version>1.0.15</version> <configuration> <!-- 原有配置 --> <reflectionList>org.jboss.resteasy.resteasy_jaxrs.i18n.Messages</reflectionList> <reflectionList>org.jboss.resteasy.resteasy_jaxrs.i18n.Messages_$bundle</reflectionList> <!-- 包含Resteasy的资源文件 --> <resourceList> <resource>org/jboss/resteasy/resteasy_jaxrs/i18n/Messages.properties</resource> </resourceList> </configuration> </plugin>
方式二:使用反射配置文件
在src/main/resources下创建reflection-config.json:
[ { "name": "org.jboss.resteasy.resteasy_jaxrs.i18n.Messages", "allDeclaredConstructors": true, "allPublicConstructors": true, "allDeclaredMethods": true, "allPublicMethods": true }, { "name": "org.jboss.resteasy.resteasy_jaxrs.i18n.Messages_$bundle", "allDeclaredConstructors": true, "allPublicConstructors": true, "allDeclaredMethods": true, "allPublicMethods": true } ]
然后在GluonFX插件中引用该文件:
<nativeImageArgs> <!-- 原有参数 --> <arg>-H:ReflectionConfigurationFiles=${basedir}/src/main/resources/reflection-config.json</arg> </nativeImageArgs>
内容的提问来源于stack exchange,提问作者Yousef Hamarsheh
相关产品推荐
相关产品推荐

