Skip to content

JEP 275: Modular Java Application Packaging | 模块化 Java 应用程序打包

摘要

将来自 Project Jigsaw 的功能集成到 Java 打包程序中,包括模块感知和自定义运行时创建。

动机

当 Java 打包程序(javapackager)在打包过程中被要求捆绑运行时时,由于 JRE 的大小,它总是生成巨大的二进制文件。Project Jigsaw 将开发一种在 JEP 282 Java 链接器 中定义的工具,允许创建包含标准和 JDK 模块子集的运行时映像,从而使 Java 打包程序可以减小捆绑运行时映像的大小。

描述

在很大程度上,Java 打包程序的工作流程将保持不变。将添加来自 Jigsaw 的新工具,并在某些情况下替代某些步骤。

只生成 Java 9 应用程序

Java 打包程序将仅创建使用 JDK 9 运行时的应用程序。这将简化与用于组装应用程序和 Java 运行时的工具相关的很多代码路径和假设。如果用户想要创建 Java 8 应用程序,则将继续使用随 JDK 8 一起提供的 Java 8 版本的 Java 打包程序。我们假设需要同时在 Java 8 和 Java 9 上工作的自包含应用程序数量基本上为零,因为该应用程序已经携带了自己的 JVM。

目前,JRE 会被复制,并从复制的运行时中删除不需要的部分。

Java 链接器工具 jlink 提供了一种生成仅包含所需模块的 JRE 映像的方法。此外,jlink 可以提供一些关于其映像生成过程的钩子,我们可以利用这些钩子进一步自定义映像,例如在 jlink 处理过程中添加可执行文件的删除或压缩。

Java 打包程序将调用 jlink 来创建一个嵌入在应用程序映像中的应用程序运行时映像。如果 jlink 失败,Java 打包程序将以适当的错误信息失败。预计打包的模块将随 JDK 9 一起提供。

jlink 工具包括插件和扩展机制。在使用 jlink 生成应用程序映像时,我们将与这些机制集成,以便 jlink 处理的输出是按正确的平台特定布局的应用程序映像。这将有一个理想的副作用,即使应用程序映像生成不依赖于 Java 打包程序进程。

javapackager 命令行参数、Ant 任务和 Java 打包程序 API

Java 打包程序具有新的命令行参数,以与 JEP 261 中指定的 Java 工具链的其余部分匹配选项语法和值:

powershell
--add-modules <module>(,<module>)*
--limit-modules <module>(,<module>)*
--module-path <path>(:<path>)*
-p <path>(:<path>)*
--module <module>/<classname>
-m <module>/<classname>

为了指定长选项的参数,您可以使用 --<name>=<value>--<name> <value>

注意: --module-path 映射到 jlink 的 --module-path,但具有可选的默认值。下面有更多信息。

将有新的 ANT 任务在 <fx:application><fx:secondaryLauncher> 和新的 <fx:runtime> 任务下。

例如:

xml
<fx:deploy outdir="${bundles.dir}"
           outfile="MinesweeperFX"
           nativeBundles="all"
           verbose="true">

    <fx:runtime strip-native-commands="false"> <-- new
        <fx:add-modules value="java.base"/>
        <fx:add-modules value="jdk.packager.services,javafx.controls"/>
        <fx:limit-modules value="java.sql"/>
        <fx:limit-modules value="jdk.packager.services,javafx.controls"/>
        <fx:module-path value="${java.home}/../images/jmods"/>
        <fx:module-path value="${build.dir}/modules"/>
    </fx:runtime>

    <fx:application id="MinesweeperFX"
                    name="MinesweeperFX"
                    module="fx.minesweeper" <-- new
                    mainClass="minesweeper.Minesweeper"
                    version="1.0">
    </fx:application>

    <fx:secondaryLauncher name="Test2"
                          module="hello.world" <-- new
                          mainClass="com.greetings.HelloWorld">
    </fx:secondaryLauncher>
</fx:deploy>

<fx:runtime><fx:limit-modules><fx:add-modules><fx:modular-path> 是可选参数。如果与模块化应用程序一起打包,则在 <fx:application> 上使用 module="module name" 参数;否则,如果应用程序是非模块化的,则无效。参数 <fx:limit-modules><fx:add-modules><fx:modular-path> 可以与本文档中使用的 --add-mods--limit-mods--module-path 互换。有关其他模块参数信息,请参见模块配置部分。

Java Packager API 将获得有关模块选项的新方法。

去除本地命令

去除命令(例如 java.exe)一直是 Java Packager 的默认设置,但某些开发人员需要命令行工具,如 java.exe。因此,将提供一种通过关闭去除命令来包含本地命令的选项:

powershell
--strip-native-commands false

添加对模块和模块路径的支持

Jigsaw 除了类路径之外还引入了“模块路径”的概念。模块路径由库、JDK 模块和应用程序模块的路径组成。包含这些模块的路径由命令行参数指定:

powershell
--module-path <path>(:<path>)*

它只能提供一次,是平台路径。根模块及其传递依赖关系被链接以创建一个模块化运行时映像(JEP 220)。

开发人员可以提供一个包含打包模块的路径,以与不同版本的 Java 运行时一起捆绑。如果开发人员未提供 JDK 打包模块,则 Java Packager 将默认使用 Java Packager 随附的 JDK 版本提供的打包模块($JAVA_HOME/jmods)。

Java Packager 当前没有提供一种机制将打包模块复制到应用程序运行时映像中,而不是链接到 'jimage' 中。这种情况最有可能需要插件支持的应用程序,并且这些模块位于打包映像之外。如果是这种情况,开发人员将需要使用用户 JVM 参数覆盖来覆盖 --module-path--add-modules

模块配置

将使用 Java Packager 打包两种类型的 Java 应用程序:非模块化的 JAR 和模块化应用程序。

非模块化的 JAR 由不包含 JAR 文件中的 module-info.class 的 JAR 组成。对于应用程序,请使用 -appClass-BmainJar=。开发人员将使用与 JDK 9 之前的先前版本相同的参数来使用 Java Packager,使用 -srcfiles-Bclasspath=-appClass-BmainJar= 参数。为了保持向后兼容性,不需要新的模块参数,并且默认情况下嵌入的 Java 运行时将包含所有可重新分发的模块,因此不会减小捆绑运行时的大小。开发人员可以使用 --module-path--add-modules--limit-modules 来包含第三方模块。

例如:

powershell
javapackager -deploy -v -outdir output -name HelloWorld -Bclasspath=hello.world.jar -native -BsignBundle=false -BappVersion=1.0 -Bmac.dmg.simple=true -srcfiles hello.world.jar -appClass HelloWorld -BmainJar=hello.world.jar

模块化应用程序由包含 module-info.class 的 JAR、解压的模块或打包的模块组成。要与模块化应用程序捆绑,必须指定 --module--module-path 参数。--module-appClass-BmainJar= 互斥。--module-path 必须提供一个包含主模块(使用 --module 引用的模块)的路径。可以使用 --add-modules--limit-modules 将其他模块添加到运行时映像中。通过核心反射或服务动态加载的模块必须使用 --add-modules 手动指定。主模块和 --add-modules 提供的模块将定义根模块。jlink 将创建一个运行时映像,包含指定的根模块及其传递依赖关系。

例如:

powershell
javapackager -deploy -v -outdir output -name Test -native -BsignBundle=false -BappVersion=1.0 -Bmac.dmg.simple=true --module-path /path/to/jmod --module hello.world/com.greetings.HelloWorld

此命令将生成一个运行时映像,其中包含主模块及其所有的传递依赖关系。可以通过 --add-modules 选项添加其他模块。

模块

打包工具将分为两个模块:

java
jdk.packager
jdk.packager.services

jdk.packager 包含构建应用程序捆绑包和安装程序的 Java Packager。jdk.packager.services 是一个与应用程序捆绑包一起捆绑的模块,在运行时提供对打包服务的访问,如 JVM 用户参数。

JNLP

生成的捆绑包将取决于输入和提供的选项。在过去,-deploy 将生成所有的本地捆绑包和 .jnlp 文件。现在,-deploy-module 结合使用将不会生成 .jnlp 文件,因为 JNLP 不支持新的模块化选项。不带选项的 -native 将生成所有可用的本地捆绑包。

测试

首先,应运行适用于 JDK 8 的 Java Packager 的现有 API、命令行和 Ant 调用的现有测试,以确保其能在 JDK 9 中正常工作。

还需要编写新的测试来测试支持生成运行时映像、模块路径规范和 jeeps 进程交互的新标志。

风险和假设

我们假设项目将按照描述的方式进行交付。如果将大量功能部分移至后续版本(例如模块路径和模块系统),则相应的 JEP 部分也会延迟。

依赖关系