Java 模块系统(JPMS)与原生打包(jlink/jpackage)

共 21 题
📑 题目列表 21 题
#
★★★

1. JPMS 模块系统(Jigsaw)解决的核心问题,强封装与显式依赖

JPMS 模块系统(Jigsaw)解决的核心问题是什么?强封装与显式依赖是什么?

  • JPMS 模块系统
  • 强封装
  • 显式依赖

JPMS 模块系统(Jigsaw)解决的核心问题:1)强封装(Encapsulation):模块明确导出哪些包(exports),未导出的包对外不可见(模块内部实现细节被封装),相比 classpath 的"全公开"更安全、可控;2)显式依赖(Explicit Dependencies):模块声明 requires 依赖(module-info),模块图明确,编译/运行时可验证依赖(避免 classpath 的隐式、混乱依赖),解决"类路径地狱"(jar 冲突、隐藏依赖)。其他:可靠配置(模块解析)、可扩展(ServiceLoader 集成)、强封装支持安全。Jigsaw 让模块化:强封装(exports 控制可见性)+ 显式依赖(requires 声明),解决 JAR 地狱与封装问题。

JPMS 用 exports 强封装(未导出包不可见)、requires 显式依赖(模块图明确),解决类路径地狱与封装问题。

module com.example.app {
    requires com.example.core;         // 显式依赖
    exports com.example.app.api;       // 导出 API 包
}
#
★★★

2. module-info.java 的 module/requires/exports/opens 声明语法与模块描述符

module-info.java 的 module/requires/exports/opens 声明语法与模块描述符是什么?

  • module-info.java
  • module/requires/exports/opens
  • 模块描述符

module-info.java 是模块描述符(module descriptor),声明模块:module 声明模块名;requires 声明依赖(requires 模块,可 requires transitive);exports 导出包(exports 包,可 exports 包 to 模块限定);opens 开放包(opens 包,允许反射访问,可 opens 包 to 模块);uses/provides 声明服务(ServiceLoader)。模块描述符编译为 module-info.class,包含模块名、依赖、导出/开放、服务声明。语法:module 块内声明各指令。体现强封装(exports/opens 控制可见性)与显式依赖(requires)。示例:module com.x { requires java.sql; exports com.x.api; opens com.x.model; }

module-info.java 是模块描述符,用 module/requires/exports/opens/uses/provides 声明模块名、依赖、导出/开放、服务。

module com.example.app {
    requires java.sql;
    requires transitive com.example.core;    // 传递依赖
    exports com.example.app.api;             // 导出包
    opens com.example.app.model;             // 开放包(反射)
    uses com.example.spi.Service;            // 服务使用
    provides com.example.spi.Service with com.example.app.ServiceImpl;
}
#
★★★

3. 模块可读性(readability)与 requires transitive 如何影响编译与运行

模块可读性(readability)与 requires transitive 如何影响编译与运行?

  • 模块可读性
  • requires transitive
  • 编译/运行

模块可读性(readability):模块 A 依赖 B(requires B),则 A 可读 B 的导出包(编译/运行可见)。requires transitive:A requires transitive B,则"依赖 A 的模块"也自动可读 B(传递可读)。影响:1)编译:requires transitive 使下游模块无需显式 requires B 即可读 B 的导出包(编译引用 B 的类型);2)运行:模块图解析时,transitive 依赖传播,下游模块可运行期访问 B。示例:模块 A requires transitive B,模块 C requires A,则 C 自动可读 B(无需显式 requires B)。作用:简化依赖声明(避免每个下游都 requires 公共依赖),用于"导出 API 依赖的模块"(如 java.sql 的 transitive)。影响编译期可见性与运行期模块图。

requires transitive 使下游模块自动可读传递依赖,简化依赖声明,impact 编译可见性与运行模块图。

module com.example.core {
    exports com.example.core.api;
    requires transitive com.example.common;   // 下游自动可读 common
}
module com.example.app {
    requires com.example.core;   // 自动可读 com.example.common
}
#
★★★

4. 未命名模块与类路径(classpath)兼容机制,自动模块(automatic module)如何生成模块名

未命名模块与类路径(classpath)兼容机制是什么?自动模块(automatic module)如何生成模块名?

  • 未命名模块
  • classpath 兼容
  • 自动模块

未命名模块(unnamed module)与 classpath 兼容:classpath 上的 jar(未模块化)属于未命名模块,未命名模块可读所有模块(读所有导出包),但模块化代码默认不能读未命名模块(除非 --add-reads 或未命名模块依赖)。自动模块(automatic module):把 classpath 上的非模块化 jar 放到 module path 上,自动成为"自动模块"(隐式模块),导出其所有包、requires 所有模块。自动模块名生成:若 jar 的 MANIFEST 有 Automatic-Module-Name 用它;否则用 jar 文件名推导(去掉版本号、.jar,把非字母数字替换为 .,如 commons-lang3-3.12.jar → commons.lang3)。自动模块用于迁移兼容(非模块化 jar 可被模块化代码依赖)。

classpath 属未命名模块(可读所有模块),非模块化 jar 放 module path 成自动模块(导出所有包),模块名用 Automatic-Module-Name 或文件名推导。

// 自动模块名:jar 文件名推导
// commons-lang3-3.12.jar -> module commons.lang3
// 或 MANIFEST: Automatic-Module-Name: org.apache.commons.lang3
#
★★★

5. exports 与 opens 的区别,反射访问为何依赖 opens 而不是 exports

exports 与 opens 的区别是什么?反射访问为何依赖 opens 而不是 exports?

  • exports 导出
  • opens 开放
  • 反射访问

exports 与 opens 区别:exports 导出包(编译期 + 运行期"正常"访问,即直接引用包中类的编译/运行),打开给其他模块"编译期可见";opens 开放包(运行时反射访问,如 setAccessible 访问私有成员),不用于编译期。反射访问为何依赖 opens:反射(setAccessible、getDeclaredField 等)访问模块的私有/非导出成员时,需要模块对该包"opens"(运行时开放),因为强封装下默认禁止反射访问未开放包;exports 只允许"正常编译/访问"(直接引用),不开放反射访问私有成员。因此反射框架(Spring/Hibernate)访问需要用 opens(或 --add-opens)。exports 是编译期可见,opens 是运行时反射开放。

exports 允许编译期正常访问,opens 允许运行时反射访问(setAccessible 私有成员),反射框架依赖 opens 而非 exports。

module com.example {
    exports com.example.api;   // 编译期可见
    opens com.example.model;   // 运行时反射开放
}
#
★★★

6. 限定导出 exports ... to 与限定打开 opens ... to,如何把内部 API 仅暴露给白名单模块,与全局导出的安全差异

限定导出 exports ... to 与限定打开 opens ... to:如何把内部 API 仅暴露给白名单模块?与全局导出的安全差异是什么?

  • exports ... to
  • opens ... to
  • 白名单

限定导出 exports ... to 模块A, 模块B 与限定打开 opens ... to 模块A 指定"只对白名单模块导出/开放",其他模块不可见/不可反射。与全局导出(exports 包)的安全差异:1)限定导出只暴露给指定模块,减少暴露面(其他模块不可见);2)限定打开只允许指定模块反射,减少反射攻击面;3)安全:限定访问控制"谁可见/谁可反射",比全局更安全(最小暴露)。示例:exports com.example.internal to com.example.plugin; 只对插件模块导出内部包;opens com.example.model to com.example.tools; 只允许工具模块反射。限定导出/打开用于"内部 API 仅暴露给可信白名单模块",减少攻击面。

exports/opens ... to 指定白名单模块(只对它们可见/可反射),比全局导出更安全(最小暴露)。

module com.example {
    exports com.example.internal to com.example.plugin;   // 只对插件导出
    opens com.example.model to com.example.tools;         // 只允许工具反射
}
#
★★

7. --add-opens/--add-exports/--add-modules 在迁移遗留应用时的使用边界与安全风险

--add-opens/--add-exports/--add-modules 在迁移遗留应用时的使用边界与安全风险是什么?

  • --add-opens/--add-exports/--add-modules
  • 迁移边界
  • 安全风险

迁移遗留应用时用 JVM 参数:--add-opens(运行时开放包给反射,如 --add-opens java.base/java.lang=ALL-UNNAMED)、--add-exports(运行时导出包给编译/访问,如 --add-exports java.base/sun.nio.ch=ALL-UNNAMED)、--add-modules(添加模块到模块路/根模块,如 --add-modules java.sql)。使用边界:1)--add-opens/--add-exports 用于"访问强封装下不可访问的 JDK 内部/模块包",是迁移的临时/必要手段;2)--add-modules 用于把模块加入模块图(如反射依赖非模块化)。安全风险:1)过度开放削弱强封装(暴露 JDK 内部/私有),增加攻击面;2)--add-exports 暴露内部 API,--add-opens 允许反射访问私有,均有风险;3)应最小化开放(只开放需要的包/模块),并尽量用公开 API 替代。迁移后应尽量移除这些参数。

--add-opens/--add-exports/--add-modules 用于迁移访问强封装内容,但过度开放削弱封装、增安全风险,应最小化并迁移到公开 API。

// 迁移参数
java --add-opens java.base/java.lang=ALL-UNNAMED \
     --add-exports java.base/sun.nio.ch=ALL-UNNAMED \
     --add-modules java.sql -jar app.jar
#
★★

8. Module Layer 与模块解析(resolve)流程,ServiceLoader 的 provides/uses 声明如何协作

Module Layer 与模块解析(resolve)流程是什么?ServiceLoader 的 provides/uses 声明如何协作?

  • Module Layer
  • 模块解析
  • provides/uses

模块解析(resolve):启动时从根模块(root modules)出发,按 requires 依赖关系解析出模块图(module graph),加载所需模块。Module Layer:模块图分层(每个模块层有自己的模块集合与类加载器),支持多个模块层(如插件、动态加载)。ServiceLoader 协作:模块用 provides 声明"为某服务提供实现"(provides Service with Impl),用 uses 声明"使用某服务"(uses Service);模块解析时,ServiceLoader 通过模块图查找 provides 的实现,动态加载服务实现。协作:provides/uses 让模块图参与服务发现(ServiceLoader 在模块化下用模块图查找实现),提供声明式服务扩展。

模块解析从根模块按 requires 构建模块图到 Module Layer,provides/uses 声明让 ServiceLoader 在模块图查找服务实现。

module com.example.app {
    uses com.example.spi.Service;                            // 使用服务
}
module com.example.impl {
    provides com.example.spi.Service with com.example.impl.ServiceImpl;  // 提供实现
}
// ServiceLoader.load(Service.class) 通过模块图查找
#
★★

9. 模块化应用与 Spring Boot 可执行 jar(非模块化)的边界,为何多数 Spring Boot 应用仍是 classpath 而非 module path

模块化应用与 Spring Boot 可执行 jar(非模块化)的边界是什么?为何多数 Spring Boot 应用仍是 classpath 而非 module path?

  • 模块化应用
  • Spring Boot jar
  • classpath vs module path

模块化应用要求所有依赖(含第三方库)都模块化(有 module-info),依赖图完整;Spring Boot 可执行 jar 是"非模块化"(fat jar,把依赖打进 jar,无 module-info)。为何多数 Spring Boot 应用仍是 classpath 而非 module path:1)第三方库大多未模块化(无 module-info),module path 无法使用;2)Spring Boot 的 fat jar(嵌套 jar、类加载器)与模块机制不兼容(模块化需真实模块路径,非嵌入 jar);3)反射/自动配置(Spring 大量反射扫描)与强封装冲突,需大量 --add-opens;4)迁移成本高、收益小。因此 Spring Boot 应用默认 classpath(未命名模块),模块化对大多数 Spring Boot 应用不适用/不必要。

Spring Boot 应用因第三方库未模块化、fat jar 与模块不兼容、反射与强封装冲突,默认 classpath 而非 module path。

// 多数 Spring Boot 应用:classpath(未命名模块)
// 原因:第三方库无 module-info、fat jar 不兼容、反射冲突
// 模块化适合:自研全模块化、运行时裁减(jlink)
#
★★

10. jlink 定制运行时镜像的裁减原理,--add-modules/--strip-debug/--compress 的工程价值

jlink 定制运行时镜像的裁减原理与 --add-modules/--strip-debug/--compress 的工程价值是什么?

  • jlink
  • 运行时镜像裁减
  • --add-modules/--strip-debug/--compress

jlink 生成定制运行时镜像(自包含 JRE),只包含应用所需模块,裁减 JDK:--add-modules 指定要包含的模块(只包含所需模块,减少运行时大小);--strip-debug 去掉调试信息(减少大小);--compress 压缩资源(zip 压缩,减小体积)。工程价值:1)体积小:只包含所需模块(+ 去调试 + 压缩),比完整 JDK 小很多;2)自包含:生成可运行镜像(含 JVM),无需安装 JDK,适合容器/分发;3)启动/管理优化。裁减原理:jlink 按模块依赖分析,只打包所需模块与类,配合 Options 减少体积。适用于模块化应用 + 容器化/分发。

jlink 按模块依赖生成定制镜像,--add-modules 只含所需模块、--strip-debug 去调试、--compress 压缩,体积小、自包含。

// jlink 生成定制运行时
jlink --module-path $JAVA_HOME/jmods:app \
      --add-modules com.example.app \
      --strip-debug --compress=2 --output myimage
#
★★

11. jpackage 打包原生安装包(msi/dmg/deb)的流程与运行时裁剪配合

jpackage 打包原生安装包(msi/dmg/deb)的流程与运行时裁剪配合是什么?

  • jpackage
  • 原生安装包
  • 运行时裁剪

jpackage(JDK 14+)打包应用为原生安装包(Windows msi/exe、macOS dmg、Linux deb/rpm):流程:1)把应用打包成模块化/非模块化 jar;2)jpackage 生成运行时镜像(基于 jlink 或完整 JRE)并打包成安装包;3)配置启动器(--java-options、--icon、--main-class 等),生成各平台安装包。运行时裁剪配合:jpackage 用 jlink 生成定制运行时(--runtime-image 或自动生成),只包含所需模块(裁减),配合安装包得到"自包含、体积小"的原生应用。流程:模块化应用 + jlink 裁减运行时 + jpackage 打包安装包。适用于桌面应用分发(无需用户装 JDK)。

jpackage 打包原生安装包(msi/dmg/deb),配合 jlink 裁减运行时生成自包含应用,无需用户装 JDK。

// jpackage 打包(配合 jlink 运行时)
jpackage --name App --input lib --main-class com.example.Main \
         --main-jar app.jar --type msi \
         --runtime-image myimage --java-options "-Xmx512m"
#
★★

12. 模块图(module graph)与循环依赖检测,模块系统如何从根源阻止模块间循环

模块图(module graph)与循环依赖检测:模块系统如何从根源阻止模块间循环?

  • 模块图
  • 循环依赖
  • 阻止循环

模块图(module graph):模块及其 requires 依赖构成的图,模块解析时构建。循环依赖检测:模块系统在解析时检测模块间的循环依赖(A requires B,B requires A),若发现循环则抛异常(ResolutionException),编译/运行期阻止。模块系统从根源阻止模块间循环:1)解析时校验模块图无环(拓扑排序/环检测);2)模块依赖必须是有向无环图(DAG),循环依赖报错;3)编译期(javac)也检测模块循环。原因:模块化设计强制依赖无环(比 classpath 隐式循环更可控),通过模块图解析检测循环,从根源禁止模块间循环依赖。

模块解析构建模块图并检测循环,模块依赖必须是 DAG(有向无环),循环依赖编译/运行期报错,从根源阻止。

// module A requires B,module B requires A
// 模块解析时报错:cycle detected(ResolutionException)
// 模块依赖必须无环(DAG)
#
★★

13. 强封装对反射框架(Spring/Hibernate/Jackson)的影响,为何 JDK 17+ 需要维护 --add-opens 清单

强封装对反射框架(Spring/Hibernate/Jackson)的影响:为何 JDK 17+ 需要维护 --add-opens 清单?

  • 强封装影响
  • 反射框架
  • --add-opens 清单

强封装(JDK 17+)默认限制反射访问 JDK 内部/私有成员,反射框架(Spring、Hibernate、Jackson)依赖反射访问 JDK 内部(如 java.base 的私有字段、Unsafe、模块内部)或频繁 setAccessible,强封装下这些访问被禁止(抛 InaccessibleObjectException)。因此 JDK 17+ 需要维护 --add-opens 清单:为框架访问的 JDK 包开放(--add-opens java.base/java.lang=ALL-UNNAMED 等),使框架能反射访问。影响:1)框架升级需适配;2)部署需配置 --add-opens 清单(列出框架访问的包);3)也可用框架的免反射方案(Hibernate 的字节码、Spring 的 MethodHandle)。维护清单是 JDK 17+ 运行传统反射框架的必要步骤。

JDK 17+ 强封装限制反射访问 JDK 内部,反射框架需 --add-opens 清单开放所需包,否则反射抛异常。

// JVM 启动需 --add-opens 清单(框架访问 JDK 内部)
java --add-opens java.base/java.lang=ALL-UNNAMED \
     --add-opens java.base/java.util=ALL-UNNAMED -jar app.jar
#
★★

14. module-info 中的版本化模块与 Maven 坐标(groupId/artifactId/version)的映射关系

module-info 中的版本化模块与 Maven 坐标(groupId/artifactId/version)的映射关系是什么?

  • 模块版本
  • Maven 坐标
  • 映射

module-info 中的模块名(module 声明)与 Maven 坐标(groupId/artifactId/version)的映射关系:module 名通常约定为"artifactId"(或包名),Maven 坐标中的 groupId/version 不直接体现在 module-info(module-info 无版本号,版本由 jar 文件名/Maven 管理)。映射:1)模块名 = artifactId(或约定的包名,如 org.apache.commons.lang3);2)groupId/version 由 Maven 管理(pom),不写入 module-info(module-info 只有模块名与依赖,无版本);3)自动模块的模块名见 Automatic-Module-Name 或文件名推导。因此模块名与 Maven 坐标不完全对应:module 名是"逻辑模块名",Maven 坐标是"构建坐标",版本由 Maven 处理。约定:模块名与 artifactId 一致便于管理。

module-info 的模块名通常等于 artifactId,groupId/version 由 Maven 管理(不写入 module-info),版本由构建管理。

// Maven: groupId=org.apache, artifactId=commons-lang3, version=3.12
// module-info: module org.apache.commons.lang3 { ... }
// 模块名 = artifactId(或 Automatic-Module-Name)
#
★★

15. jdeps 在模块化迁移中的用法,分析 jar 依赖、生成 module-info 建议并输出 jlink 所需的最小模块集

jdeps 在模块化迁移中的用法:分析 jar 依赖、生成 module-info 建议并输出 jlink 所需的最小模块集是什么?

  • jdeps
  • 依赖分析
  • 模块化迁移

jdeps 是 JDK 依赖分析工具,用于模块化迁移:1)分析 jar/class 的依赖(jdeps jar):输出类依赖、包依赖、JDK 模块依赖;2)生成 module-info 建议(jdeps --generate-module-info):分析依赖并生成 module-info.java 建议(推断模块名、requires/exports);3)输出 jlink 所需最小模块集(jdeps --print-module-deps):列出应用所需的 JDK 模块子集,供 jlink --add-modules 使用。用法:迁移时用 jdeps 分析依赖、生成模块建议、确定最小模块集,辅助模块化改造与 jlink 裁减。jdeps 是模块化迁移与 jlink 的关键工具。

jdeps 分析 jar 依赖、--generate-module-info 生成 module-info 建议、--print-module-deps 输出最小模块集,辅助模块化迁移与 jlink。

jdeps --print-module-deps app.jar          // 输出所需 JDK 模块
jdeps --generate-module-info out app.jar   // 生成 module-info 建议
jlink --add-modules $(jdeps --print-module-deps app.jar) ...
#
★★

16. --patch-module 的用途与边界,测试与热修复时覆盖模块内类,与 classpath 覆盖、jlink 镜像修改的区别

--patch-module 的用途与边界:测试与热修复时覆盖模块内类,与 classpath 覆盖、jlink 镜像修改的区别是什么?

  • --patch-module
  • 覆盖模块类
  • 与 classpath/jlink 区别

--patch-module 用于"覆盖/补充模块内类":指定替换/补充某个模块的类(如 --patch-module java.base=patch.jar 用 patch.jar 覆盖 java.base 的类)。用途边界:测试(mock 模块类)、热修复(修复模块 bug 类)、补充类。与 classpath 覆盖的区别:classpath 覆盖(早加载类)依赖类加载顺序,不可靠;--patch-module 明确把补丁类应用到模块,可靠。与 jlink 镜像修改的区别:jlink 镜像修改是"重新生成镜像"(静态、打包时),--patch-module 是"运行时通过命令行参数补丁"(动态、不改镜像)。--patch-module 用于运行时覆盖模块类,测试/热修复,比 classpath 可靠、不改 jlink 镜像。

--patch-module 运行时覆盖模块类(测试/热修复),比 classpath 覆盖可靠,且不改 jlink 镜像(动态补丁)。

// 运行时覆盖 java.base 的类
java --patch-module java.base=patch.jar -jar app.jar
// 与 classpath 覆盖(不可靠)区别、与 jlink 重建(静态)区别
#

17. 多版本 jar(Multi-Release JAR)与 META-INF/versions 的机制与应用场景

多版本 jar(Multi-Release JAR)与 META-INF/versions 的机制与应用场景是什么?

  • Multi-Release JAR
  • META-INF/versions
  • 应用场景

多版本 jar(Multi-Release JAR,MR-JAR)允许一个 jar 针对不同 JDK 版本提供不同类实现:jar 的 MANIFEST 声明 Multi-Release: true,META-INF/versions/9、versions/11 等目录放特定 JDK 版本的类,运行时按 JDK 版本选择对应实现(高版本用 versions 下的类,低版本用根目录类)。机制:JDK 运行时按版本加载 META-INF/versions/ 的类覆盖根类。应用场景:1)库需要针对不同 JDK 版本提供不同实现(如利用新 JDK API 优化,同时兼容旧版本);2)新特性(如 String 相关、模块)分版本实现。MR-JAR 让库在 JDK 6-25 间兼容并利用新版本特性。

MR-JAR 的 MANIFEST 声明 Multi-Release,META-INF/versions/ 按 JDK 版本提供类实现,用于库跨版本兼容并利用新特性。

// MANIFEST.MF: Multi-Release: true
// META-INF/versions/9/... 高版本类
// 运行时按 JDK 版本选择对应实现
#

18. 模块描述符的 uses/provides 声明与 SPI 的编译期校验

模块描述符的 uses/provides 声明与 SPI 的编译期校验是什么?

  • uses/provides
  • SPI 编译期校验
  • 模块服务

模块描述符的 uses(声明使用某服务接口)与 provides(声明提供某服务实现)用于模块化 SPI(ServiceLoader)。编译期校验:javac 在编译时校验 uses/provides 的正确性:1)uses 的服务接口、provides 的实现类必须存在且由模块导出(可读);2)provides 的实现类必须实现服务接口(类型校验);3)provides 的 service 类型必须是接口/抽象类(有实现)。编译期校验保证 SPI 声明正确,防止运行时 ClassCastException/NoClassDefFound。与 ServiceLoader.load 协作:模块化下 ServiceLoader 通过模块图的 uses/provides 查找实现。编译期校验让 SPI 声明类型安全。

uses/provides 声明模块化 SPI,javac 编译期校验服务接口/实现类存在、类型正确,保证 SPI 声明安全。

module com.example.app {
    uses com.example.spi.Service;                              // 声明使用
    provides com.example.spi.Service with com.example.impl.ServiceImpl;  // 提供实现
}
// 编译期校验:接口存在、实现实现接口
#

19. Project Jigsaw 的模块化迁移策略,自上而下(top-down)与自下而上(bottom-up)两条路径

Project Jigsaw 的模块化迁移策略:自上而下(top-down)与自下而上(bottom-up)两条路径是什么?

  • 自上而下迁移
  • 自下而上迁移
  • 迁移策略

Project Jigsaw 模块化迁移两条路径:1)自上而下(top-down):先模块化应用的顶层(应用模块),依赖的底层库先用自动模块(automatic module)或未命名模块,逐步向下模块化。适用于"应用层希望先模块化"(顶层有 module-info,底层用自动模块过渡);2)自下而上(bottom-up):先模块化底层库(先给库加 module-info),再逐层向上模块化应用。适用于"底层库先模块化"(库作者提供模块化)。两条路径都从"未命名模块 + 自动模块"过渡,逐步加 module-info。选路径依据:依赖关系(底层先自下而上,应用先自上而下)。迁移核心:渐进式模块化,用自动模块/未命名模块过渡。

自上而下先模块化应用顶层(底层用自动模块过渡),自下而上先模块化底层库,渐进式迁移用自动模块过渡。

// 自上而下:应用模块 -> 底层自动模块
// 自下而上:底层库 module-info -> 上层
// 迁移用自动模块(module path 放非模块化 jar)过渡
#

20. JMOD 格式与 jmod 工具,模块打包与 jar 的差异(含 native 代码与 legal 文件),何时使用 jmod

JMOD 格式与 jmod 工具:模块打包与 jar 的差异(含 native 代码与 legal 文件),何时使用 jmod?

  • JMOD 格式
  • jmod 工具
  • 与 jar 差异

JMOD 是模块打包格式(jmod 工具生成,.jmod 文件),与 jar 差异:1)jmod 可包含 native 代码(native libraries)、legal 文件(license)、配置文件,jar 只含 class 与资源;2)jmod 用于"模块在模块路径(jlink )、编译期"而非运行期(运行期用 jar);3)jmod 格式(压缩、含模块描述符)与 jar 不同。何时使用 jmod:1)打包含 native 代码/legal 文件的模块(JDK 模块、系统模块用 jmod);2)jlink 需要的模块(jmod 包含 native);3)应用模块分发(模块作者用 jmod 打包 native/legal)。运行期应用用 jar,包含 native 的模块用 jmod(供 jlink)。

jmod 可含 native 代码/legal 文件,用于 jlink/编译期/系统模块,运行期用 jar;含 native 的模块用 jmod。

// 生成 jmod:含 native 代码、legal 文件
jmod create --class-path classes --lib native --legal-notices legal com.example.app.jmod
#

21. jpackage 的启动器配置,--java-options 注入堆大小等 JVM 参数、图标与安装包元数据,以及跨平台打包的差异

jpackage 的启动器配置:--java-options 注入堆大小等 JVM 参数、图标与安装包元数据,以及跨平台打包的差异是什么?

  • jpackage 启动器配置
  • --java-options
  • 图标/元数据/跨平台

jpackage 启动器配置:--java-options 注入 JVM 参数(如 --java-options "-Xmx512m -Dfile.encoding=UTF-8");--icon 指定应用图标;--app-version/--vendor/--description 等元数据;--main-class/--main-jar 指定入口。跨平台打包差异:1)Windows 生成 msi/exe(--type msi),配置快捷方式、图标(.ico);2)macOS 生成 dmg/.app(--type dmg),配置 .icns 图标、签名;3)Linux 生成 deb/rpm(--type deb/rpm),配置 .png 图标、desktop 条目。跨平台差异:安装包格式、图标格式、签名、元数据字段不同,jpackage 按平台生成对应格式。--java-options 注入 JVM 参数 + 图标/元数据 + 平台差异配置启动器。

--java-options 注入 JVM 参数、--icon/--app-version 配置图标元数据,跨平台差异(Windows msi/exe、macOS dmg、Linux deb/rpm)按平台生成。

// Windows
jpackage --type msi --icon app.ico --java-options "-Xmx512m" --app-version 1.0 ...
// macOS
jpackage --type dmg --icon app.icns --java-options "-Xmx512m" ...
// Linux
jpackage --type deb --icon app.png --java-options "-Xmx512m" ...