JFX11+Maven+IDEA 发布跨平台应用的完美解决方案 - 知乎
org.openjfx
javafx-base
11
linux
org.openjfx
javafx-base
11
win
org.openjfx
javafx-controls
11
linux
org.openjfx
javafx-controls
11
win
org.openjfx
javafx-fxml
11
linux
org.openjfx
javafx-fxml
11
win
org.openjfx
javafx-graphics
11
linux
org.openjfx
javafx-graphics
11
win
UTF-8
11
11
-------------------------------------------------------------------------------------------------------------------------------
原文
前几天写了两篇关于JFX+IDEA打包跨平台应用的文章,这篇是使用IDEA自带功能打包的,这篇是使用Maven进行打包的,但是效果不太满意,因为从JDK9开始实现模块化,同时JFX部分从JDK中独立出来了,也就是说需要默认JDK不再自带JFX。这意味着外部依赖需要手动处理module-info.java
,这是一件非常麻烦的事情。
其实不使用Maven
也能打包发布跨平台JFX应用,但是没有使用Maven
的话,虽然打包出来能直接运行无需jre环境,但是,管理依赖确实麻烦,在使用jlink
打包一些外部的jar时,对于一些比较简单的jar还是比较舒服的,参照这里:
首先去下载jar,接着生成module-info.java
,然后使用jdeps
检查依赖,添加对应的jar到路径中,编译生成module-info.java
接着更新原来的jar即可。看起来简单,但是笔者碰到了okhttp这种jar,依赖简直环环相扣导致笔者放弃了这种方式。
Maven
使用Maven
可以完美解决依赖问题,多亏强大的pom.xml
,几行
就可以解决依赖问题,但是,还是需要手动处理module-info.java
,而且IDEA文档明确表明仅支持Java8的打包为jar:
因此,这篇文章采取一种最简单的方式利用Maven
打包发布JFX11应用。
Maven
工程默认即可,问题不大。
org.openjfx
javafx-base
11
linux
org.openjfx
javafx-base
11
win
org.openjfx
javafx-controls
11
linux
org.openjfx
javafx-controls
11
win
org.openjfx
javafx-fxml
11
linux
org.openjfx
javafx-fxml
11
win
org.openjfx
javafx-graphics
11
linux
org.openjfx
javafx-graphics
11
win
需要在哪个平台在classifier
中指定即可。这里是Linux
与win
。
同时指定编码与JDK:
UTF-8
11
11
否则会报错:
新建一个包再新建Main.java
、Launcher.java
以及Main.fxml
:
Main.java
如下:
package com.test;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/Main.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Hello World");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Launcher.java
:
package com.test;
public class Launcher {
public static void main(String[] args) {
Main.main(args);
}
}
Main.fxml
:
注意getResource
中的fxml
路径,Main.fxml
文件放在resources
下,直接通过根路径读取:
getResource("/Main.fxml");
此时应该是没有运行配置的状态,点击Add Configuration
:
添加Application
:
添加Launcher
类作为Main class
:
这时候run
就没问题了:
Maven
打包虽然现在可以运行了,但是,如果直接使用默认的Maven
打包的话:
在target
下有一个jar,直接右键运行:
会提示no main manifest attribute
:
也就是找不到Manifest
中入口类。
jar实际上是一个class的压缩包,与zip的区别是jar包含了一个MANIFEST.MF
,MANIFEST.MF
在META-INF
下,一个示例文件如下:
有点类似与键值对的格式,MANIFEST.MF
包含了jar文件的内容描述,并在运行时向JVM提供应用程序信息。注意该文件有严格的格式限制,比如第一行不能为空,行与行之间不能存在空行。
一个暴力的解决办法是直接解压jar并修改里面的MANIFEST.MF
,添加
Main-Class: com.test.Launcher
但是这样会报找不到Application类的异常:
理论上来说,只需要jar包内的相同目录下提供了javafx的jar或者class文件就不会抛出异常了,但是,如果依赖很多需要一个一个添加,这是一个痛苦的过程。
所以,为了优雅地解决这个问题,引入一个叫maven-shade-plugin
的插件即可:
org.apache.maven.plugins
maven-shade-plugin
3.2.2
package
shade
com.test.Launcher
最新版本请到官方Github查看,使用时只需要修改:
xxx.xxx.xxx
修改为程序入口类。
此时再从右侧栏打包选中Maven
,package
即可:
但是会有警告:
因为一些class文件重复了,但是也提到了通常来说这是没有危害的并且可以跳过警告,或者修改pom.xml
去手动排除某些依赖。
直接在IDEA中右键运行或者-jar运行,可以看到没有异常了:
相比起原来自带的Maven
打包插件,主要是多了javafx的一些class以及对应平台所需要的一些动态库文件等,比如Windows
上的.dll
与Linux
上的.so
文件。
这样一个跨平台的JFX jar包就制作好了,只需
java -jar
即可跨平台运行。