总所周知 Java
基于 JVM
实现了跨平台,即一次编译任意平台运行,只需 JRE
运行环境即可。
跨平台的特性也是市面一系列产品如 IDEA
以及 DBeaver
等应用选择 Java
作为开发语言的一大原因。
虽然 Java
提供了跨平台的功能,但默认编译后的程序为 jar
文件,在发布时显然不够优雅。常常我们会通过各类工具将其二次编译为对应系统可直接运行的程序,例如在 Windows
下编译为 exe
程序,Mac
环境下的 dmg
等等。
但 Java
默认并不提供 exe
打包方式,需要借助其它工具。以 Windows
平台为例,可以选择 exe4j
工具,如果是新项目则可采用 GraalVM
编译为 native image
运行,二者都可生成 exe
可运行程序。
Exe4j
与 GraalVM
虽然都可实现 exe
打包,但各有优缺点。前者默认打包后启动默认自带弹窗提示,后者则对旧项目并不友好。
而今天所分享的 JavaPackager
则可通过 Maven Plugin
插件的方式无感集成项目,提供 Windows
、Linux
与 Mac
多平台程序构建,下面就让我们直接开始把。
1. 项目准备
在开始之前,让我们先准备一个简单的 Swing
程序。
在之前的 Maven
教程中提过,项目默认在打包 jar
文件时并不会将所带的依赖一并编译,常通过 assembly
等插件实现 fat jar
构建。
而在 JavaPackager
中已经提供了此功能,因此无需额外引入其它插件,这里引入 Jackson
为例演示打包后的效果。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.19.0</version>
</dependency>
完成后让我们实现一个简单的 Swing
窗口程序。
程序内容并不复杂,通过 Jackson
序列化 Map
实例并展示的串口中,完整代码如下:
public class PkgClient {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws JsonProcessingException {
JFrame frame = new JFrame("PKG Client");
frame.setSize(300, 120);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
// 添加内容
frame.add(createTextPanel());
frame.setVisible(true);
}
private static JPanel createTextPanel() throws JsonProcessingException {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
Map<String, String> map = Map.of(
"id", "123",
"name", "Alex"
);
// 添加文本
JLabel label = new JLabel(mapper.writeValueAsString(map), SwingConstants.CENTER);
label.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(label);
return panel;
}
}
运行程序,可以得到下图中效果:
2. 基础编译
那么现在我们就可以执行编译工作,先以最简化的配置开始,编译 Windows
下的 exe
程序。
在项目的 pom
文件中添加 javapackager
插件,其中 phase = package
即表示作用于 mvn package
打包期间。
<build>
<plugins>
<plugin>
<groupId>io.github.fvarrui</groupId>
<artifactId>javapackager</artifactId>
<version>1.7.6</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>package</goal>
</goals>
<configuration>
<name>PkgClient</name>
<mainClass>xyz.ibudai.pkg.PkgClient</mainClass>
<platform>windows</platform>
<bundleJre>true</bundleJre>
<jrePath>C:\Program Files\Java\jdk-17.0.7\jre</jrePath>
<createZipball>true</createZipball>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
上述配置中核心的配置项说明参考下表:
方法 | 作用 |
---|---|
name | 编译后的目录以及应用名,默认为类名以 - 分隔。 |
mainClass | 主类的完整限定名。 |
platform | 目标平台,可选值:auto|linux|mac|windows |
bundleJre | 是否生成 JRE,若否编译后文件允许需系统带 JRE。 |
jrePath | 若 bundleJre 为 true,可指定 jre 目录。 |
createZipball | 是否生成 -platform 后缀的 zip 文件,解压即可运行。 |
添加配置后执行 mvn package
命令后即会在 target
目录生成下述内容。
图中 -windows.zip
文件解压缩后内容与 PkgClient
目录内容一致,包含下述文件:
其中 exe
即可执行程序,libs
即项目所依赖的 jar
包,在上述的工程中即 Jackson
相应 jar
文件。
而 jre
目录即 Java
运行环境,通过 bundleJre
参数控制是否生成,若否则生成的打包文件中不会包含这部分内容,也就意味在运行时需要对应的系统配置 Java
环境。默认其为拷贝当前系统 Java
所配置的版本,当然你也可以通过 jrePath
指定版本。
3. 环境变量
运行 Java
程序时,在部分场景下常常我们会通过 -D
等等方式添加变量,亦或调整 JVM
参数。
在让 JavaPackager
编译后的文件运行时添加启动参数十分简单,在生成的 exe
下添加同级文件 xxx.l4j.ini
文件即可,其中 xxx
为上述 name
标签配置的值。
例如上述示例中,则新加 PkgClient.l4j.ini
文件后放于 exe
同级即可,文件中的内容按照一行一条的格式。
-Xms16m
-Dswing.aatext=true
-Dsomevar="%SOMEVAR%"
如需要添加参数 info.message=hello
,则在文件中添加下述内容:
-Dinfo.message=hello
稍微调整之前的程序,在窗口中读取配置并展示,代码片段如下:
private static JPanel createTextPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
String message = System.getProperty("info.message", "default message");
// 添加文本
JLabel label = new JLabel(message, SwingConstants.CENTER);
label.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(label);
return panel;
}
修改后重新执行 package
编译项目,创建 PkgClient.l4j.ini
文件置于 exe
同级下并运行程序,可以得到下述效果:
4. 文件拷贝
在部分需求场景下,我们的项目会依赖外部文件,在打包时需要一并进行复制。
针对此类场景,JavaPackager
同样提供了 additionalResources
标签用于文件或目录复制。
假如我们需要将 assets
目录一并复制进打包生成的目录结构中,则修改 pom
配置为下述内容:
<configuration>
<name>PkgClient</name>
...
<additionalResources>
<additionalResource>D:\Temporary\assets</additionalResource>
</additionalResources>
</configuration>
完成后重新执行 packge
命令构建,在生成的结构下可以看到 assets
目录生成成功。
这里需要注意一点,.l4j.ini
配置文件默认是通过 additionalResources
实现,若自定义了 additionalResources
属性且仍需要运行参数,则需要显式在 additionalResource
配置 .l4j.ini
文件路径。
参考链接