使用Java 9的模块化来构建零依赖的原生应用

Java模块化开发的依赖管理策略:从混乱到优雅 Java模块化依赖管理指南摘要 本文系统讲解Java 9+模块化依赖管理方案,通过module-info.java实现显式声明、服务解耦与安全控制。核心要点: 模块声明:使用exports/requires/opens控制访问权限,provides/uses实现服务动态发现 冲突解决:通过Maven/Gradle统一版本或重构模块结构 构建工具集成:演示多模块项目的POM/Gradle配置 服务模式实战:展示接口-实现-消费者的完整解耦案例 配套代码示例涵盖模块定义、依赖传递、服务加载等关键场景,帮助开发者 阅读详情

原文: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。它会生成一个包,包括:

  1. 应用程序代码及其依赖关系;
  2. 一个嵌入式Java运行时环境;
  3. 本地启动器(例如,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/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;
}

这里的CLI应用程序只是对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文件:

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压缩后的大小
cli21.7 MB10.8 MB
gui45.8 MB29.1 MB

这是在Windows机器上,用64位的JRE打包打出来的结果(Linux包的大小有点大,但比例仍大致相同)。下面是一些注意事项:

  • 作为比较,这个平台上完整的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等热点数据库技术展开,从核心技术的深挖到高可用实践的剖析,打造精华压缩式分享,举一反三,思辨互搏,报名及更多详情可点击此处查看。

Java 9 模块化:如何使用模块化提升代码管理? 大家好!随着软件工程和项目规模的不断扩大,代码库的复杂性也在不断增加。你可能会发现,随着项目的推进,管理代码变得越来越棘手。尤其是当涉及到多个模块、类、依赖关系时,如何保证代码的结构清晰,管理得当,成为了每个开发者必须面对的问题。曾经,在开发过程中,随意引用外部库,随时扩展代码是常见的做法。但随着开发周期延长,这种方式开始暴露出问题:依赖混乱、版本冲突、冗余代码等一系列问题层出不穷。每次修改一处代码,可能会导致其他模块不可预期的崩溃,这显然不利于长期维护。为了应对这些问题,Java 9 引入了模块化。 阅读详情

相关推荐

Java 9 模块化:如何使用模块化提升代码管理

在 Java 9 之前,Java 应用程序的构建和管理是基于包(Package)的,而模块化的引入,使得 Java 程序可以按功能进行组织。一个模块是一个包含包、类、接口和其他资源的自包含单位。每个模块都有一个明确的依赖声明,并且能够清晰地暴露哪些资源可以被其他模块访问。

shrgegrb的博客 1125

【Java 基础篇】Java 模块化详解

Java 模块化是自 Java 9 版本开始引入的一项重要特性,它将应用程序划分为一组模块,提供了更好的组织和管理代码的方式。通过明确的模块边界和依赖关系,我们能够更好地组织大型项目、提供更好的封装和可重用性,并提升应用程序的性能和安全性。在开发大型 Java 项目时,使用模块化是一个值得考虑的选择。希望本文对你理解和使用 Java 模块化提供了帮助。如果你对 Java 模块化还有其他疑问,欢迎留言讨论。

繁依Fanyi的博客 4390

Java 9模块化编程

Java 9引入了模块化系统(Project Jigsaw),旨在改善Java应用的可维护性、性能和安全性。模块化允许开发者将代码划分为不同的模块,模块之间可以明确声明依赖关系,从而实现更好的封装、重用和管理。在这一新特性中,JDK自身也被模块化,允许开发者根据需要构建定制化的JRE。本文将介绍Java 9模块化的引入与概念、模块定义与模块路径、模块之间的依赖关系以及如何使用JLink工具构建自定义JRE。

喵手的博客 626

Java高级——模块化系统

在JDK 9以后,若启用了模块化封装,模块可声明对其他模块的显式依赖,JVM在启动时验证依赖关系,若缺失则启动失败,从而避免部分由于依赖而引发的运行时异常。JDK 9之前基于类路径(ClassPath)来查找依赖,若缺失,则等程序运行到该类型的加载、链接时才会报出运行时异常。所以也不支持多版本模块并存、运行时热替换、热部署模块的能力,仍须通过类加载器去实现(将OSGi和JPMS混合使用)为了保证使用传统类路径依赖的Java程序可以不经修改地直接运行在JDK 9及以后的版本,制定以下规则。

松仔Log的博客 1802

泛微E9开发 明细表字段赋值到主表字段,将各类型合计赋值到主表类型合计中

①绑定明细表类型、预计金额字段的值改变事件,每当有值发生改变,就需要重新计算供应商1、供应商2、供应商3的合计,如果不绑定这两个字段的话,会出现计算不及时或计算错误的问题。②当有明细行被删除时,也需要重新计算供应商1、供应商2、供应商3的合计,比如删除了供应商1的明细行,如果不重新计算,那么供应商1的值还是被删之前的值,合计错误。将各类型合计赋值到主表类型合计中。

a_waste_only的博客 3836

灰度发布、蓝绿部署、双活架构与数据同步/备份/迁移

本文系统介绍了现代互联网系统的核心部署策略与数据管理技术体系。主要内容包括:1)三种服务发布策略(灰度发布、蓝绿发布、滚动发布)的原理、实现方式和适用场景对比;2)高可用架构设计(同城双活、异地双活、异地多活)的技术方案;3)数据同步机制、备份策略与迁移方案的设计要点。文章通过架构图、流程图和配置示例详细展示了各类技术的实施方法,并分析了不同方案的优劣及风险控制措施,为构建高可用、高可靠的分布式系统提供了完整的技术指南。

zane的博客 515

Java 模块化

Java 模块化的设计目标是提供一种更好的项目组织和管理方式,解决依赖管理、可重用性、可扩展性和安全性等方面的问题,使得开发者能够更有效地构建和维护大型和复杂的 Java 应用程序。其实 Java 官方发布这个特性至今为止很少有人用到 Java 原生的模块系统,取而代之的是各种包管理工具提供的类似的特性。例如 Maven、Ant 或者 Gradle 这些包管理器都提供了类似的概念,而且到 Java 17 为止这个特性确实没有很出色的表现(至少作者没有看到)。但既然作为新特性,我们仍然有学习的必要。

a1518967800的博客 1016

树莓派与无人机协同实战:基于OpenCV的地标识别优化策略

本文深入探讨了基于树莓派与无人机的协同系统,重点分享了利用OpenCV优化地标识别以实现精准降落的实战策略。内容涵盖从应对复杂光照的图像预处理、鲁棒的轮廓几何过滤,到通过ROS进行稳定通信,以及实现分层控制与状态机设计,旨在提升系统在真实环境中的可靠性与性能。

y2z3a4b5c的博客 1086

带你了解“Java新特性——模块化”

Java平台从Java 8向Java 9及更高版本的进化,其中引入了一个重要的新特性——模块系统(Project Jigsaw)。模块系统的目的是解决大型应用的依赖管理问题,提升性能,简化JRE,增强兼容性和安全性,并提高开发效率。通过模块化,Java能够更好地支持微服务架构,提供更细粒度的封装和控制,以及更清晰的依赖关系。文章详细介绍了模块系统的概念,如MODULE DESCRIPTOR、主要参...

淘系技术 2736

基于STM32的电子琴音乐播放器设计

​ 本设计以实际电路为最后的结果,实现了所有与其功能,具有重要的现实意义。件,在Proteus中进行原理图绘制,然后把 .hex文件下载到STM32F103RBT6中 进行仿真。由于网络上 下载的 Proteus中 没有 STM32F103RBT6这个芯片型号,我们又从网上 下载芯片 进行仿真。​ 本设计以实际电路为最后的结果,实现了所有与其功能,具有重要的现实意义。

TMMXA的博客 2万+

内网Linux服务器离线安装python包

3、将下载的依赖包和requirements.txt 拷贝到内网主机,依赖包和requirements.txt放同一级目录,进入该目录,运行指令。其实还有个更加方便的办法,联网主机装个与内网主机一样环境的虚拟机,在虚拟机中下载py包,拷贝到内网主机后就能直接安装了,省去手动下载py包的步骤。简单记录下最近遇到的一个小问题,内网主机没有内网源,处于无互联网状态,安装python包只能通过联网主机下载好后拷贝到内网主机。由于内网主机安装了多个版本的python,为了防止搞错,使用这种方式来调用pip安装。

cyc头发还挺多的博客 650

java 模块依赖_使用Java 9的模块化来构建零依赖的原生应用

摘要:本文通过实例介绍了如果通过Java 9的模块化特性来构建一个独立的、零依赖的可执行程序。以下是译文。“为什么没办法创建一个.EXE程序?”在Java刚刚出现的时候,主流的编程语言要么可以编译为独立的可执行文件(例如C/C++、COBOL),要么运行在解释器中(例如Perl、Tcl)。对于大部分的程序员来说,Java对字节码编译器和运行时解释器的需求让他们开始转变自己的思维。编译模型使得Jav...

weixin_35441226的博客 402

[OrCad电路板设计系统].Orcad.Pspice.9.2下载安装

Orcad.Pspice.9.2详细下载安装图文步骤1 安装前准备工作1.1 下载安装包1.2 解压2 安装时步骤2.1 安装文件2.2 破解文件3 安装完成 1 安装前准备工作 1.1 下载安装包 打开百度网盘APP 链接:https://pan.baidu.com/s/1cMFxHUvuh5iEhFVT-lvciQ 提取码:7m65 1.2 解压 解压下载好的安装包 2 安装时步骤 2.1 安装文件 1.点击Setup安装文件,出现警告对话框,告诉你应该关闭所有病毒防火墙,点击确定 2.进入安装界面,

weixin_48924581的博客 1万+

java有非标准库生成exe_使用Java 9的模块化来构建零依赖的原生应用

作者:Steve Perkins翻译:雁惊寒摘要:本文通过实例介绍了如果通过Java 9的模块化特性来构建一个独立的、零依赖的可执行程序。以下是译文。“ 为什么没办法创建一个.EXE程序? ”在Java刚刚出现的时候,主流的编程语言要么可以编译为独立的可执行文件(例如C/C++、COBOL),要么运行在解释器中(例如Perl、Tcl)。对于大部分的程序员来说,Java对字节码编译器 和 运行时解释...

weixin_39914107的博客 152

YOLO11改进 | 注意力机制 | 添加SimAM注意力机制【全网独家+附完整代码】

yolo11,yolo11改进,yolo11创新,yolo11添加注意力机制

kay_545 1230

置顶必读(1) |《滚雪球学SpringBoot 3.x》· 高质量专栏介绍,你值得拥有!

《滚雪球学SpringBoot 3.x》 并不是普通的技术讲解合集,它是专为开发者精心设计的系统化学习路径。通过不断“滚动学习”,我们将带领你从基础入门到高阶应用,从理解每一行代码到设计和优化大规模分布式系统,最终帮助你成长为一位精通 spring架构 的工程师。

**My Coding Family** 12万+

java安装_使用Java 9模块化来发布零依赖本机应用程序

java安装为什么我不能仅构建一个.EXE? 首次引入Java时,主流编程语言大多要么编译成独立的可执行文件(例如C / C ++,COBOL),要么在解释器中运行(例如Perl,Tcl)。 对于许多程序员而言,Java对字节码编译器和运行时解释器的需求是思想上的转变。 编译模型使Java比“脚本”语言更适合于业务编程。 然而,运行时模型要求在每台目标计算机上部署合适的JVM并使其可用。 人...

cunfeng7797的博客 253

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Java9模块化(Jigsaw)结合javafx应用,创建零依赖桌面应用程序

Java9模块化(Jigsaw)结合javafx应用,创建零依赖桌面应用程序 首先说明编译环境: java11 + idea java的桌面应用现在首选的肯定是javafx.在java8时,javafx与JDK结合,使得不需要额外下载SDK即可使用,但是java9后,JDK模块化,使得javafx从中分离,现在我们用模块化构建javafx应用时会出现很多的问题,这篇文章记录下我碰到这些问题的解决方...

qq_34330381的博客 1499

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【java9】java9新特性之模块化

Java9模块化是Java9版本中引入的一个重要特性,通过Java Platform Module System (JPMS)实现。这个特性允许开发者将大型应用程序拆分成相互独立的模块,每个模块包含一组相关的功能和资源。

morris 1万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

java 9 模块化最详细讲解

java9模块化如何使用?java9模块化有哪些申明?模块化跟maven有什么区别?史上最详细讲解

java那些事儿 1527

java9之模块化

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34377919的博客 2477
上一篇: Go语言中的常见陷阱
下一篇: 容器化RDS|计算存储分离架构下的IO优化
csdn研发技术
博客等级 码龄9年 1381粉丝 90原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值