JavaPackager 使用教程


总所周知 Java 基于 JVM 实现了跨平台,即一次编译任意平台运行,只需 JRE 运行环境即可。

跨平台的特性也是市面一系列产品如 IDEA 以及 DBeaver 等应用选择 Java 作为开发语言的一大原因。

虽然 Java 提供了跨平台的功能,但默认编译后的程序为 jar 文件,在发布时显然不够优雅。常常我们会通过各类工具将其二次编译为对应系统可直接运行的程序,例如在 Windows 下编译为 exe 程序,Mac 环境下的 dmg 等等。

Java 默认并不提供 exe 打包方式,需要借助其它工具。以 Windows 平台为例,可以选择 exe4j 工具,如果是新项目则可采用 GraalVM 编译为 native image 运行,二者都可生成 exe 可运行程序。

Exe4jGraalVM 虽然都可实现 exe 打包,但各有优缺点。前者默认打包后启动默认自带弹窗提示,后者则对旧项目并不友好。

而今天所分享的 JavaPackager 则可通过 Maven Plugin 插件的方式无感集成项目,提供 WindowsLinuxMac 多平台程序构建,下面就让我们直接开始把。

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 文件路径。


参考链接


文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录