jpackage是java 14里面自带的打包工具,jpackage解决了java开发过程中得一个难题:分发自己的程序,需要客户电脑中已安装jre环境。有了jpackage,我们可以直接将java程序打包成安装包,具体来说:
- Windows:exe,msi
- Mac:dmg,pkg
- Linux:deb,rpm
jpackage目前并不成熟,但是也算是可以使用。另外,虽然jpackage可以打包各个系统的安装包,但是在一个系统上只能打对应系统的安装包。比如在windows上,就只能打成exe或msi。
额外建议:jpackage打包,涉及到java 9中的模块概念以及如何使用jlink自定义jre的知识,请自行学习。推荐阅读:
- 模块-廖雪峰
- Java9新特性系列(模块化系统: Jigsaw->Modularity)
- 我的Java(定制你的Java/JavaFX Runtime)
本文主要介绍如何使用jpackage打包,以及在打包过程中遇到的各种坑。
1. 环境准备·
要使用jpackage打包,请确保:
- 安装好jdk 14,配置好JAVA_HOME和PATH
- windows平台,请安装wix3
安装好,打开cmd,执行jpackage -h:
如果有这样的显示,那说明安装成功。注意首行的WARNING,这个不用管,因为jpackage目前还属于一个孵化阶段,所有有这样的提示。
另外,下文中的所有程序都可放在了github,地址:
https://github.com/raven-misc/jpackage-demo
2. 非模块程序打包·
先以非模块程序为例,新建一个java项目,得到如下目录:
代码:
1 | package com.raven; |
功能很简单,让系统发出beep声。
然后将这个项目打包成jar包,可以通过命令,也可以直接通过idea,自行选择,我这里采用idea:
测试一下通过java -jar能否运行:
好的,现在我们将这个jar包打成安装包:
在根目录下建立一个lib,并把Non-modular-packging-demo.jar复制进lib目录下:
2.1 安装包·
在根目录下,打开你的terminal,执行(命令是执行不成功的,会报311错误,但是先执行看看):
1 | jpackage --name Non-modular-installer --input lib --main-class com.raven.App --main-jar Non-m |
- –name 打包后的安装包名
- –input 要打包的文件目录
- –main-class 这是非必须选项,如果你的jar包META-INF/MANIFEST.MF中已经指定了Main-Class, 则无需此命令。
- –main-jar 主程序所在jar包
- 其余常用:
- –temp 临时文件所在目录,默认系统temp
- –dest 打包到哪个目录下,默认当前目录
- –type 打包成什么类型,exe?msi?deb等等,windows默认是exe
不过在我使用这条命令时(2020/5/5),报了311错误,源头来自wix。解决方案,在命令后指定vendor,如:
1 | jpackage --name Non-modular-installer --input lib --main-class com.raven.App --main-jar Non-m |
ok,已经打包成功,试试安装吧(提示,又会有想不到的效果)。
相信你通过双击安装,已经发现了问题,那就是安装后没有任何效果,没有windows-menu,没有windows快捷键,程序也没有运行启动。但是控制面板已经找到安装程序了。我一度以为是自己电脑环境的问题,最后发现在windows平台上还需加上几条命令才行:
1 | jpackage --name Non-modular-installer --input lib --main-class com.raven.App --main-jar Non-modular-packaging-demo.jar --vendor raven --win-dir-chooser --win-shortcut --win-menu --win-menu-group "Non-modular-packaging" |
解释:
-
–win-dir-chooser, 安装时添加“选择安装路路径”
-
–win-shortcut,安装后自动在桌面添加快捷键
-
–win-menu-group,启动该应用程序所在的菜单组(实测无效,但是必须有这条命令,没有–win-menu会报311错误)
update 2021-2-18: –win-menu-group应该放在–win-menu之后,否则无效。
-
–win-menu,添加到系统菜单中
现在双击启动程序试试,应该就有beep了。再看看安装目录:
2.2 便携版(无需安装)·
jpackage提供一个选项,可以用来生成镜像(image),而这个镜像就可以充当便携版执行命令和上面基本一致,添加–type app-image命令,删除所有–win-xxx即可:
1 | jpackage --name Non-modular-installer --type app-image --input lib --main-class com.raven.App --main-jar Non-modular-packaging-demo.jar --vendor raven |
内部目录和安装包安装后的目录完全一致,所以直接压缩这个文件夹就有便携版了。
2.3 便携版–>安装包·
我们打包成镜像后,还可以将镜像转化为安装包(通常发生在我们想对镜像做一些定制化内容时),执行命令:
1 | jpackage --name Non-modular-installer --type msi --app-image Non-modular-installer --vendor raven |
这里打包成msi:
3. 模块程序打包·
通过maven新建JavaFX项目,得到如下目录:
运行一下,看下效果:
模块化程序打包就略微复杂一些了,首先明确一点,idea中,有maven在,所有的编译输出不在out目录下,而在target目录下:
除此之外,还需要明确我们当前的模块名:
当前模块名为app。
2.1 字节码打包·
首先确定依赖的modules,在module-info.java文件中,
生成如下的依赖图:
不用关注java开头和jdk开头的模块,因为它们是系统自带的。也就是所当前app模块直接或间接依赖了:
- javafx.controls
- javafx.graphics
- javafx.base
三个模块,现在我们要找到这个三个模块jar包路径,如何找?
在这上面右键–>Show in Exploer即可。
可以看到导航到了我们的maven仓库中,分别记下这三个jar包的路径,如下:
1 | C:\Users\Raven\.m2\repository\org\openjfx\javafx-controls\14\javafx-controls-14 win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-base\14\javafx-base-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-graphics\14\javafx-graphics-14-win.jar |
现在都是自己手动找的,之后会介绍插件,自动寻找。
ok,准备工作就绪,开始打包。在target目录下,打开terminal:
执行命令:
1 | jpackage --name "Modular-installer" --module-path classes;C:\Users\Raven\.m2\repository\org\openjfx\javafx-controls\14\javafx-controls-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-base\14\javafx-base-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-graphics\14\javafx-graphics-14-win.jar -m app/com.raven.App --vendor raven |
解释:
- –name 应用程序名
- –module-path: 也就是前面让你找的几个依赖模块的jar包路径(以;间隔)+classes(自己的模块)
- -m: 指定主模块及主程序。 app/com.raven.App, "/“前是模块名,”/"后是主程序全路径。
现在还会遇到的问题,在”非模块程序打包“下都已经作了说明,现在补全所有命令:
1 | jpackage --name "Modular-installer" --module-path classes;C:\Users\Raven\.m2\repository\org\openjfx\javafx-controls\14\javafx-controls-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-base\14\javafx-base-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-graphics\14\javafx-graphics-14-win.jar -m app/com.raven.App --vendor raven --win-dir-chooser --win-shortcut --win-menu --win-menu-group "Modular-packaging" |
2.2 通过jar包打包·
注意到我们上面module-path中有一项是classes,这就是我们自己的模块,我们也可以先将整个模块打包成jar包,然后替换classes。
有maven,打包就是再简单不过的事了,
重新执行命令:
1 | jpackage --name "Modular-installer" --module-path Modular-packaging-demo-1.0-SNAPSHOT.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-controls\14\javafx-controls-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-base\14\javafx-base-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-graphics\14\javafx-graphics-14-win.jar -m app/com.raven.App --vendor raven --win-dir-chooser --win-shortcut --win-menu --win-menu-group "Modular-packaging" |
2.3 打包成镜像(便携版)·
同”非模块程序打包“一样,添加–type,删除–win-xx
1 | jpackage --name "Modular-installer" --module-path Modular-packaging-demo-1.0-SNAPSHOT.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-controls\14\javafx-controls-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-base\14\javafx-base-14-win.jar;C:\Users\Raven\.m2\repository\org\openjfx\javafx-graphics\14\javafx-graphics-14-win.jar -m app/com.raven.App --vendor raven --type app-image |
2.4 定制jre,自定义jlink,插件化(推荐打包方式)·
在jpackage打包模块化程序时,其实底层调用了jlink,比如我们的–module-path参数,其实时会传递给jlink的。当我们需要对jlink做一些自定义参数时,可以先调用jlink生成自定义的jre,然后通过这个jre来打包。
前面的打包过程,有一个非常头疼的问题,那就是需要我们自己去寻找依赖,然后找到这些一些的路径。(当然其实下载jmods也是可以的,但是还是麻烦的),还好我们有插件辅助。
打开pom文件,会看到这个插件:
1 | <plugin> |
注意版本一定要是0.0.3+。然后点击:
会得到:
在image/bin中打开terminal,执行java --list-modules
命令:
1 | app |
上面就是我们这个jre所包含的所有模块。可以和我们之前做依赖分析的图做对比,是不是完全相同?接着我们通过jre来进行打包。
回到target目录:
1 | jpackage -n Modular-packaging-demo --type msi --runtime-image image --vendor raven |
老规矩,如果需要menu,shortcut,自己添加。
细心的同学会发现一个问题,那就是我们手动打包的exe文件对比通过jre打包的文件,体积会小很多。这怎么办呢?
回想一下,为什么我们要通过插件来打包,因为我们不想手动找各个模块的依赖路径。换句话说,只要我能够方便的获取模块的依赖路径就好了。
好,那怎么办呢?
其实通过maven+debug就好了。再次在target目录下打开terminal:
1 | mvn clean -X javafx:jlink |
-X打开debug。最后会得到:
这就是我们的依赖路径了,提取出来,再手动执行jpackage就ok了。
4. 参考·
- JEP 343: Packaging Tool (Incubator)
- JavaFX环境搭建&部署打包
- Brief Example Using the Early Access jpackage Utility
总算是写完了,从学习modular到遇到各种坑,花了差不多整整一天,就为了打包javafx。总体来说,jpackage还不算成熟,仍有些bug,比如为什么一定要添加vendor,又如为什么–win-menu一定要和–win-menu-group配套。但总体来说能使用。