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。
使用 jlink
来生成嵌入式 Java 运行时和应用程序映像
目前,JRE 会被复制,并从复制的运行时中删除不需要的部分。
Java 链接器工具 jlink
提供了一种生成仅包含所需模块的 JRE 映像的方法。此外,jlink
可以提供一些关于其映像生成过程的钩子,我们可以利用这些钩子进一步自定义映像,例如在 jlink
处理过程中添加可执行文件的删除或压缩。
Java 打包程序将调用 jlink
来创建一个嵌入在应用程序映像中的应用程序运行时映像。如果 jlink
失败,Java 打包程序将以适当的错误信息失败。预计打包的模块将随 JDK 9 一起提供。
jlink
工具包括插件和扩展机制。在使用 jlink
生成应用程序映像时,我们将与这些机制集成,以便 jlink
处理的输出是按正确的平台特定布局的应用程序映像。这将有一个理想的副作用,即使应用程序映像生成不依赖于 Java 打包程序进程。
javapackager
命令行参数、Ant 任务和 Java 打包程序 API
Java 打包程序具有新的命令行参数,以与 JEP 261 中指定的 Java 工具链的其余部分匹配选项语法和值:
--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>
任务下。
例如:
<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。因此,将提供一种通过关闭去除命令来包含本地命令的选项:
--strip-native-commands false
添加对模块和模块路径的支持
Jigsaw 除了类路径之外还引入了“模块路径”的概念。模块路径由库、JDK 模块和应用程序模块的路径组成。包含这些模块的路径由命令行参数指定:
--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
来包含第三方模块。
例如:
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
将创建一个运行时映像,包含指定的根模块及其传递依赖关系。
例如:
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 选项添加其他模块。
模块
打包工具将分为两个模块:
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 部分也会延迟。