解锁Java打包新姿势

2,535 阅读11分钟

在平时我们打包会将其打成Jar,那么在其他平台运行的时候就需要安装jre来支持运行。

那么实际上Java是可以打包成native平台所属类型的,例如:

  • exe
  • installer
  • image
  • msi
  • dmg
  • rpm
  • deb

当然我在查阅了很多博客,及其官网的信息,

见到最多的是exe4j,但是看到有一些评论说,打包之后无法运行,而且我下载了exe4j之后是不能运行的。所以排除此选项。

除了exe4j呢还有,install4j,jwrapper等工具,但是他们都是收费的,所以也排除。

还有Java9的jaot,早期版本的java14的jpackage,java8的javapackager

在查阅的博客中有人提到了java9的jaot,下载了java9,但是并没有找到jaot,考虑到常用版本是8,所以也排除了jaot

那么只剩下java14的jpackage和java8的javapackager,这两个我都有过实践,效果几乎一直,打出来的包大小也差不多(没有对jre做过任何裁剪的情况下)。

最终选择了Java8自带的javapackager这个工具。(但是遗憾的是,无法显示控制台的内容,建议使用日志文件去代替。java14的jpackage是可以指定是否显示控制台)

现在我们就来看看,如何将jar包打包成native形式的包。

准备内容

  • Inno Setup Compiler(Java8支持6版本以下的,不要下载6及其6以上的版本,否则无法打包成功)

  • wix(打包成msi必须下载,没有下载javapackager会提示缺少wix)

  • java8的环境

  • 示例的jar程序(本文使用这个jar作为示例,方便交流学习)

    程序介绍

    启动程序之后,会在c盘创建一个名为我是测试文件.txt的文件,每秒会给文件中写入”休眠*秒“的字样,直到10s之后程序运行结束,结尾会有写入成功的字样。(如果启动之后没有生成该文件,请在任务管理器找到该进程结束,然后以管理员身份运行该程序。)

让我们开始第一个打包吧!

在此之前,默认您准备内容已经阅读并且完成了。

  1. 进入你下载jar的包的文件夹,并且打开powershell

  2. 输入:

    javapackager -deploy -native image  -outdir cs -outfile cs -srcfiles pack.jar -appclass cn.qs.CS -name CS
    
  3. 你会在同级目录找到一个叫cs的文件夹,进入cs,再进入bundles文件夹。找到cs.exe,用管理员身份运行。

  4. 进入C盘就能找到该程序生成的我是测试文件.txt的文件

至此,简单的打包结束。

现在我们来看一下上面的命令代表什么意思。

javapackager---java8自带的打包程序

-deploy---用来构建目标机器的发行版本,简单说就是打包成exe或者其他平台的包,如果不带任何参数,会生成一个基本的应用程序,不建议不带任何参数

-native image---为jar创建磁盘镜像(可以将image替换为其他的类型,例如exe,msi,deb,rpm)

-outdir cs---输出目录。

-outfile cs----输出文件(不要带后缀,比如cs.exe,就写cs就行了)。

-srcfiles pack.jar---要打包的jar文件。

-appclass cn.qs.CS---jar文件的主类的全限定名。(注意是全限定名)

-name CS---启动之后的应用名称

更多的参数将会在文章最后附上。也可以直接输入javapackager来查看帮助,或者进入官网查看

来做一些定制吧

目标

  1. 修改应用图标
  2. 修改应用说明(按照官网操作,指定了但是无效,可以忽略)
  3. 修改应用程序的供应商(vendor)

注意:因为是控制台程序呢,所以 标题,宽,高指定的话也没什么意义。所以我们在此就修改这两个属性就可以了。

我们开始定制吧

注意:测试过image的形式,描述(description)和说明(vendor)是无效的,在exe和msi形式下是可以的

  1. 在下载的jar的同级目录,删掉刚才生成的cs文件夹。

  2. 重新执行命令:

    javapackager -deploy -native exe  -outdir cs -outfile cs -srcfiles pack.jar -appclass cn.qs.CS -name CS -description "测试的描述" -vendor "测试厂商" -Bicon=".\cs.ico"
    
  3. 现在我们就定义成功了。(可以自行看一下,是不是我们自定义的图标,说明,供应商) 关于更多自定义的话请查阅官网(这些自定义的bundle参数在使用的时候要注意,-B加上参数名=值 例如 icon 使用的时候就是 -Bicon=“path”)

bundle参数说明

除了上述打包bundle参数之外呢,在介绍一些通用的bundle参数和windows的bubdle参数。

通用参数

  1. appVersion=version 应用的版本,例如:-BappVersion=1.0.1
  2. classPath=path 类路径相对于打包后应用的目录(默认不用设置。)
  3. icon=path 应用图标的路径,例如:-Bicon=".\cs.ico"
  4. identifier 用于其他特定平台的默认值。
  5. jvmOptions=option 用来指定jvm参数,例如:-BjvmOptions=-Xmx128m -BjvmOptions=-Xms128m
  6. jvmProperties=property=value 当程序运行的时候,这个参数会被传入到jvm中,可以通过System.getProperty()来获取,java -Dproperty 都可以使用,需要指定参数名和值,如果要传递多个请参考:-BjvmProperties=apiUserName=example -BjvmProperties=apiKey=abcdef1234567890
  7. mainJar=filename 包含主类的jar文件的名称,该文件名通常是从jar文件的mainfest中获得,如果你使用javapackager命令,不用指定这个值
  8. preferencesID=node 用来检查用户可覆盖的jvm选项,被指定的选项在运行时作为 -Dapp.preferences.id.参数传给应用,这个参数需要和userJVMOptions一起使用。
  9. runtime=path Jdk或者jre的位置,如果使用系统默认的jre则不需要提供。例如:-Bruntime=
  10. userJvmOptions=option=value 能被用户覆盖的jvm选项 java命令所使用的任何选项都是有效的,要传入多个,请参考: -BuserJvmOptions=-Xmx=128m -BuserJvmOptions=-Xms=128m

windows

  1. copyright=string 应用的版本字符串,例如:2002-2020 Jetbrains s.r.o.

  2. licenseFile=path 最终用户许可协议的位置(EULA),例如 -BlicenseFile=COPYING

  3. menuHit=boolean

    标志着是否在开始菜单或开始屏幕上安装快捷方式,设置为true来安装快捷方式,默认是true 例如:-BmenuHint=true

  4. shortcutHint=boolean 标志着是否在桌面安装快捷图标,设置为true表示安装,默认是false。例如:-BshortcutHint=true

  5. systemWide=boolean 标志着应用程序是安装在Programs Files中还是在UserData中,设置为true安装在Programs Files,默认为false

  6. win.menuGroup=group menuGroup只有在menuHint为true的时候才生效,menuHint为false这个参数会被忽略掉。

  7. vendor=value 也就是作者,可以是个人,组织,公司等。用于exe,或者registry metadata.

参数简述

用法: javapackager -command [-options]

其中 command 为以下项之一:
  -createjar
          打包程序根据其他参数生成 jar 档案。
  -deploy
          打包程序根据其他参数生成 jnlp 和 html
          文件。
  -createbss
          将 css 文件转换为二进制形式
  -signJar
          使用提供的证书为 jar 文件签名。
  -makeall
          将 compilation, createjar 和 deploy 步骤作为一个调用执行,
          在其中预定义大多数参数。源必须位于 "src"
          文件夹中, 生成的文件 (jar, jnlp, html) 将放入 "dist"
          文件夹中。此命令只能以最简单方式配置, 并且
          尽可能自动进行。

createjar 命令的选项包括:
  -appclass <application class>
          要执行的应用程序类的限定名称。
  -preloader <preloader class>
          要执行的预加载器类的限定名称。
  -paramfile <file>
          包含默认命名应用程序参数的属性文件。
  -argument arg
          JNLP 文件的 <fx:argument> 元素中要放入的未命名
          参数。
  -classpath <files>
          相关 jar 文件名列表。
  -manifestAttrs <manifest attributes>
          附加清单属性列表。语法: "name1=value1,
          name2=value2,name3=value3"。
  -noembedlauncher
          如果存在, 打包程序将不会向 jarfile 添加 JavaFX
          启动程序类。
  -nocss2bin
          在复制到 jar 之前, 打包程序不会将 CSS 文件转换为
          二进制形式。
  -runtimeversion <version>
          所需 JavaFX 运行时的版本。
  -outdir <dir>
          要将输出文件生成到的目录的名称。
  -outfile <filename>
          生成的文件的名称 (不带扩展名)。
  -srcdir <dir>
          待打包文件的基目录。
  -srcfiles <files>
          srcdir 中的文件的列表。如果省略, 将对 srcdir (在
          这种情况下是必需的参数) 中的所有文件进行打包。

deploy 命令的选项包括:
  -title <title>
          应用程序的标题。
  -vendor <vendor>
          应用程序的供应商。
  -description <description>
          应用程序的说明。
  -appclass <application class>
          要执行的应用程序类的限定名称。
  -preloader <preloader class>
          要执行的预加载器类的限定名称。
  -paramfile <file>
          包含默认命名应用程序参数的属性文件。
  -htmlparamfile <file>
          包含所生成小应用程序参数的属性文件。
  -width <width>
          应用程序的宽度。
  -height <height>
          应用程序的高度。
  -native <type>
          生成自包含的应用程序包 (如果可能)。
          如果指定了类型, 则只创建此类型的包。
          所支持类型的列表包括: installer, image, exe, msi, dmg, rpm 和 deb。
  -name <name>
          应用程序的名称。
  -embedjnlp
          如果存在, 会将 jnlp 文件嵌入 html 文档中。
  -embedCertificates
          如果存在, 会将证书嵌入 jnlp 文件中。
  -allpermissions
          如果存在, 应用程序将需要 jnlp 文件中的所有
          安全权限。
  -updatemode <updatemode>
          设置 jnlp 文件的更新模式。
  -isExtension
          如果存在, srcfile 将被视为扩展。
  -callbacks
          在生成的 HTML 中指定用户回调方法。格式为
          "name1:value1,name2:value2,..."
  -templateInFilename
          html 模板文件的名称。占位符格式为
          #XXXX.YYYY(APPID)#
  -templateOutFilename
          要将已填充的模板写入到的 html 文件的名称。
  -templateId
          用于模板处理的应用程序的应用程序 ID。
  -argument arg
          JNLP 文件的 <fx:argument> 元素中要放入的未命名
          参数。
  -outdir <dir>
          要将输出文件生成到的目录的名称。
  -outfile <filename>
          生成的文件的名称 (不带扩展名)。
  -srcdir <dir>
          待打包文件的基目录。
  -srcfiles <files>
          srcdir 中的文件的列表。如果省略, 将对 srcdir (在
          这种情况下是必需的参数) 中的所有文件进行打包。

createbss 命令的选项包括:
  -outdir <dir>
          要将输出文件生成到的目录的名称。
  -srcdir <dir>
          待打包文件的基目录。
  -srcfiles <files>
          srcdir 中的文件的列表。如果省略, 将对 srcdir (在
          这种情况下是必需的参数) 中的所有文件进行打包。

signJar 命令的选项包括:
  -keyStore <file>
          密钥库文件名。
  -alias
          密钥的别名。
  -storePass
          用于检查密钥库完整性或对密钥库取消锁定的口令。
  -keyPass
          用于恢复密钥的口令。
  -storeType
          密钥库类型, 默认值为 "jks"。
  -outdir <dir>
          要将输出文件生成到的目录的名称。
  -srcdir <dir>
          待签名文件的基目录。
  -srcfiles <files>
          srcdir 中的文件的列表。如果省略, 将对 srcdir (在
          这种情况下是必需的参数) 中的所有文件进行打包。

makeAll 命令的选项包括:
  -appclass <application class>
          要执行的应用程序类的限定名称。
  -preloader <preloader class>
          要执行的预加载器类的限定名称。
  -classpath <files>
          相关 jar 文件名列表。
  -name <name>
          应用程序的名称。
  -width <width>
          应用程序的宽度。
  -height <height>
          应用程序的高度。
  -v      启用详细输出。

示例用法:
--------------
javapackager -createjar -appclass package.ClassName
  -srcdir classes -outdir out -outfile outjar -v
          将类目录的内容打包到 outjar.jar,
          将应用程序类设置为 package.ClassName。
javapackager -deploy -outdir outdir -outfile outfile -width 34 -height 43
  -name AppName -appclass package.ClassName -v -srcdir compiled
          在由 package.ClassName 类启动且大小为 34x43 的
          应用程序 AppName 的 outdir 中生成
          outfile.jnlp 和对应的 outfile.html 文件。
javapackager -makeall -appclass brickbreaker.Main -name BrickBreaker
  -width 600 -height 600
          此命令执行包含编译在内的所有打包工作:
          compile, createjar 和 deploy。

结语

经过这一番操作,其实应该对打包有一定的个人理解了。

也有的人提出创建一个bat来启动java程序,好处不言而喻,但是也有弊端。例如:在任务管理器里面显示的进程是java进程,那么如果有多个这样的程序呢,就会导致分不清进程。而且需要依赖JRE。

那么在这种打包方式中呢,存在很多弊端,例如,包体积太大,但是解决了上面的弊端问题,而且在java9中可以使用jlink来对jre进行裁剪,包体积会进一步缩小,个人更倾向于第二种打包方式,如何抉择,请结合业务场景和需求。

关于javapackager打包相关的内容,请参考官网

遇到问题,请在下方评论留言。