Using Java 9 Modularization to Ship Zero-Dependency Native Apps
作者:Steve Perkins
翻译:雁惊寒
摘要:本文通过实例介绍了如果通过 Java 9 的模块化特性来构建一个独立的,零依赖的可执行程序.以下是译文.
" 为什么没办法创建一个. EXE 程序?"
在 Java 刚刚出现的时候,主流的编程语言要么可以编译为独立的可执行文件(例如 C/C++,COBOL),要么运行在解释器中(例如 Perl,Tcl).对于大部分的程序员来说,Java 对字节码编译器和运行时解释器的需求让他们开始转变自己的思维.编译模型使得 Java 比 "脚本" 语言更适合于业务编程.然而,运行时模型则需要在每台目标机器上部署和使用合适的 JVM.
人们对此有些抵触.在早期的网络论坛,以及后来的 StackOverflow 问题中,为了避免在目标机器上安装 Java 运行时,开发者们都在寻找某种能将 Java 应用程序发布为 "原生" 可执行文件的方法.
解决办法从一开始就有. Excelsior JET 是一个超前的 Java 编译器,提供了部分 C++ 风格的体验.然而,它的许可费用高达数千美元.当然,也有免费工具,例如 Launch4j 和 JDK 8 的 javapackager 工具.这些工具能帮你把 Java 运行时环境与启动程序捆绑在一起,以使用该 JRE 来启动应用程序.但是,嵌入 JRE 会使应用程序增加大约 200MB.由于技术上的原因以及许可问题,应用程序的大小很难降下来.
Java 9 来了
Java 9 中最广为人知的新功能是新的模块化系统,称为 Jigsaw 项目 .简而言之,这个新模块用于隔离代码块及其依赖关系.
这个模块不仅适用于外部库,也适用于 Java 标准库本身.这意味着应用程序可以声明自己需要标准库的哪些部分,并排除所有其他的部分.
这个功能是通过 随 JDK 一同提供的 jlink 工具来实现的.乍一看,jlink 类似于 javapackager.它会生成一个包,包括:
应用程序代码及其依赖关系;
一个嵌入式 Java 运行时环境;
本地启动器(例如,bash 脚本或 Windows 批处理文件),用于通过嵌入式 JRE 启动应用程序.
jlink 建立了 "链接时" 这个新的可选阶段,它处于编译时和运行时之间,用于执行优化,例如删除不可达的代码.与捆绑整个标准库的 javapackager 不同,jlink 把精简过的 JRE 和仅包含应用程序所需的那些模块捆绑在一起.
示例
jlink 和老版本的 javapackager 之间的区别非常的大.为了说明这一点,我们来看一个示例项目:
https://github.com/steve-perkins/jlink-demo
(1)创建一个模块化的项目
这个代码库包含一个多项目的 Gradle 构建. cli 子目录中是一个 "Hello World" 命令行程序,而 gui 是一个 JavaFX 桌面应用程序.请注意,对于这两者,通过在 build.gradle 文件中配置下面这一句来让每个项目与 Java 9 兼容:
sourceCompatibility = 1.9
下面将创建 module-info.java 文件,为每个项目设置模块化.
这里的 CLI 应用程序只是对
/cli/src/main/java/module-info.java:
}
module cli {
/gui/src/main/java/module-info.java:
module gui {
requires javafx.graphics;
requires javafx.controls;
exports gui;
}
System.out.println()
的调用,所以它只依赖于 java.base 模块(这是隐式的,不需要声明).
但并不是所有的应用程序都使用 JavaFX,所以这里的 GUI 应用程序必须声明它对 javafx.graphics 和 javafx.controls 模块的依赖.而且,由于 JavaFX 的工作方式不太一样,所以底层库需要访问我们的代码.export gui 这一行赋予了这个库的可见性.
Java 开发人员(包括我自己)需要一些时间来感悟新的标准库模块以及它们包含的内容. JDK 包含的 jdeps 工具 可以帮忙用来解决这个问题.而且只要某个项目被设置为模块化,IntelliJ 就能识别出丢失的声明并协助开发人员自动完成它们.即使 Eclipse 和 NetBeans 没有类似的支持,它们很快也会添加进去的.
(2)构建一个可执行的 JAR
要使用 jlink 构建一个可部署的包,首先要将应用程序打包成一个可执行的 JAR 文件.如果项目依赖第三方库,那么需要使用 "shaded" 或 "fat-JAR" 插件来生成包含所有依赖关系的单个 JAR.我们这个例子只使用了标准库,所以构建一个可执行的 JAR 是一件非常简单的事情,只需让 Gradle 的 jar 插件包含一个声明可执行类的
META - INF / MANIFEST.MF
文件:
(3)运行 jlink
jar {
manifest {
attributes 'Main-Class': 'cli.Main'
}
}
据我所知,Gradle 还没有一个插件能够提供干净并且可以无缝集成的 jlink.所以,我的构建脚本使用 Exec 任务来运行该工具.命令行调用是这样的:
[JAVA_HOME] / bin / jlink--module - path libs: [JAVA_HOME] / jmods--add - modules cli--launcher cli = cli / cli.Main--output dist--strip - debug--compress 2--no - header - files--no - man - pages
--module-path 标志类似于 CLASSPATH.它告诉工具应该在哪里查找已编译的模块二进制文件(即 JAR 文件或新的 JMOD 格式文件).在这里,我们告诉它寻找项目的 libs 子目录(因为这是 Gradle 放置可执行 JAR 的地方)以及标准库模块的 JDK 目录.
--add-modules 标志用于声明哪些模块要添加到结果包中.我们只需要声明自己项目的模块即可(cli 或 gui),因为它所依赖的模块将作为传递依赖被引入.
生成的包将包含 / bin 子目录,用于执行应用程序的 bash 脚本或 Windows 批处理文件. --launcher 标志允许你为这个脚本指定一个名字,以及它应该调用哪个 Java 类(这看起来有点多余,因为这已经在可执行 JAR 中指定了).在上面的命令中,我们将创建一个名为 bin/cli 的脚本,它将调用模块 cli 中的 cli.Main 类.
--output 标志直观地指定了放置结果包的子目录.在这里,我们使用一个名为 dist 的目标目录.
最后,这些标志 --strip-debug,--compress 2,--no-header-files 和 --no-man-pages 是我所做的一些优化,以减少产生的包的大小.
在项目的根目录下,这个 Gradle 命令建立并链接了两个子项目:
./gradlew linkAll
由此产生的可部署包可以在以下位置找到:
结果
[PROJECT_ROOT]/cli/build/dist
[PROJECT_ROOT]/gui/build/dist
我们来看看链接出来的 CLI 和 GUI 应用程序的大小,以及精简的嵌入式 JRE:
应用 原始大小 用 7-zip 压缩后的大小
这是在 Windows 机器上,用 64 位的 JRE 打包打出来的结果(Linux 包的大小有点大,但比例仍大致相同).下面是一些注意事项:
21.7 MB 10.8 MB
45.8 MB 29.1 MB
作为比较,这个平台上完整的 JRE 大小是 203MB.
用 Go 编写的 "Hello World" 命令行程序编译出来是 2MB 左右. Hugo 是用于发布此博客的网站生成器,它是一个 27.2MB 大小的 Go 可执行文件.
对于跨平台的 GUI 开发,一个典型的 Qt 或 GTK 应用程序仅附带大约 15MB 的 Windows DLL. Electron 快速开始例程 会生成一个 131 MB 的程序.
结论
说句公道话,一个包含启动脚本的应用程序包并不像 " 单单一个. EXE 程序 " 那样简单.另外,由于 JIT 编译器的原因,JRE 在启动时相对较为缓慢.
即便如此,Java 仍然是一种能够生成大小与其他编译语言相媲美,独立的,零依赖的应用程序的语言(并且比 Electron 等 web 混合程序更为优秀).此外,Java 9 包含了一个实验性的 AOT 编译器 ,这也许可以加速程序的启动.尽管这个 jaotc 工具最初只适用于 64 位 Linux 平台,但它很快就会扩展到其他平台.
虽然 Go 在早期的云基础设施 CLI 工具中备受瞩目(例如 Docker , Kubernetes , Consul , Vault 等),但 Java 正在成为一个强有力的替代者,特别是对于具有构建 Java 经验的项目组.对于跨平台桌面 GUI 应用程序,我认为 JavaFX 与 Java 9 模块化的结合是现在最好的选择.
1 月 13 日, SDCC 2017 之数据库线上峰会 即将强势来袭,秉承干货实料(案例)的内容原则,邀请了来自阿里巴巴,腾讯,微博,网易等多家企业的数据库专家及高校研究学者,围绕 Oracle,MySQL,PostgreSQL,Redis 等热点数据库技术展开,从核心技术的深挖到高可用实践的剖析,打造精华压缩式分享,举一反三,思辨互搏,报名及更多详情可 点击此处查看 .
来源: http://blog.csdn.net/dev_csdn/article/details/79020557