Jigsaw 项目另起炉灶

  • Alex Blewitt
  • 郭晓刚

2013 年 9 月 2 日

话题:Java语言 & 开发

Mark Reinhold 在jigsaw-dev 邮件列表上发言表示,Jigsaw 项目将在一个新的 Hg 仓库上另起炉灶,再次尝试解决让 JDK 实现模块化的难题。新仓库位于http://hg.openjdk.java.net/jigsaw/jake

Jigsaw 项目迁延日久,向前可追溯到最初的JSR 277和和稍晚的JSR 294,原本设定的目标是将 Java 的核心运行时库解耦,拆分为若干模块。如果成功实现的话,纯服务器端的 JVM 运行的时候将终于可以不必带着用不上的 AWT 支持。

对 JDK 进行模块化拆分,这件事情很不简单。例如按照现在的设计,java.beansjava.appletjava.awt包之间依次存在依赖关系。即使我们真的能够在那么多年后推翻原来的设计决策,取消它们的依赖关系,也很难不顾此失彼。

Java 其实老早就有一个模块系统,起初叫做JSR 8,也就是现在的 OSGi 和JSR 291。这个系统被所有的主流 Java EE 应用所采用,在其他领域的系统和应用中也运用得非常广泛(如 JIRA、Eclipse),还进入了某些特定的垂直市场(如嵌入式系统、家庭自动化)。在这样的背景下,Jigsaw 项目学习java.util.Date、Java IO NIO NIO2 NIO2.2、java.logging包等前辈的榜样,丢开最佳实践,存心凑合一套给 JDK 自己用的系统。(好在 Java 8 总算收拾了java.util.Date这个怪胎,纳入了 Joda-Time,也就是新的 java.time 包来作为对java.util.Date的修正的修正。)

每一回 JSR 277/Jigsaw 推倒重来,目标都要打一点折扣。最早的时候,它是一个适用于任何应用的通用模块系统,且具备解析链和仓库存储机制。(照 Java 的惯例,系统会设计成可插拔的,我们会见到 ModuleFactory 和 ModuleResolverFactory。)差不多 5 年前的2008 年 12 月,JSR 277 好像走进了死胡同。于是项目的目标经过重新评估,一种可以聚合多个包,但只对外暴露单一个包或单一组 API 的“超级包”概念被提了出来,从而诞生了 JSR 294。

到了 JSR 294 也徘徊不前的时候,又诞生了 Jigsaw 项目,目标相应地变更为实现一个主要为 JVM 本身服务的模块系统(但同时为用于其他系统留有余地)。Jigsaw 放弃了一些理所当然的特性,如模块解析链,只定位在给 Java 提供基本的模块定义能力。Jigsaw 提出的方案犯了一个关键错误,它让模块通过执行代码的方式,而非声明式的方式来表达依赖关系,其结果造成我们不可能通过静态分析来确定一个模块系统成立与否。(不能静态检查约束条件是否满足的模块系统,有一个现成的例子,也就是我们熟悉的“classpath”,现在通常可以在小规模的应用和一些 IDE 中见到。)

大约 1 年前,Mark Reinhold 撰文“Project Jigsaw: Late for the Train”,宣布 Jigsaw 项目将错过 Java 8 的发布日程。这件事情的反响毁誉参半,OSGi 的支持者和反对者们各自宣告胜利或失败;Jigsaw 推迟到 Java 9 才定案,好歹让该项目有机会修正一些错误决策。

这一次另起炉灶,Mark Reinhold 希望拿出一个不那么雄心勃勃的系统原型,一方面保留现有的由解析代理来提供各种 JAR 的方式,另一方面新增一种静态描述元素间模块依赖关系的手段。眼下新仓库中的代码仅仅是从 JDK8 拷贝过来的一个分支,但按照打算,这次会抛弃那些一直以来拖后腿的包袱,在上一轮尝试中成为问题根源的一些设计决策,将得到重新审视。

我们的计划之一,是避免像当前原型那样引入专门的“模块模式”(此模式与一些由来已久的习惯做法向左,会导致某些不普遍、但有深刻影响的兼容问题),也不负责解析依赖关系(因为这件事情 Maven、Ivy、Gradle 等构建工具已经做得够好了),在这样的前提下验证可行的方案。

我们的新原型会从旧的原型中借鉴合理的代码,但最重要的还是借机重估原来的一些设计决策,并且做一次全面的清理。

尚待决断的重要设计决策有:

  • 版本号是遵守一种现成的版本号格式系统,如Semantic Versioning,还是编到哪算哪
  • 模块系统是(像 Ivy、Maven 和 Gradle 那样)声明式地表述依赖关系,还是必须通过执行代码来确定依赖关系(并因此阻碍静态分析和事先核验)
  • 模块系统是(像 OSGi 那样)动态的,还是(像 Maven 那样)静态的;换言之,模块可以随增随减,还是只进不出
  • 赖关系的元数据是放在 JVM 能理解的.class 文件里,还是用 JSON、YAML、Manifest.MF 之类的文本格式来保存

以 Oracle 对外来方案的排斥,选择 OSGi 作为 Jigsaw 实现基础的可能性很低。另一方面,OSGi 作为一种完全动态的方案,用作 JVM 的模块化机制也显得过火。最好的折中方案可能是一种近似于 OSGi,具有版本化的依赖项管理和 bundle 机制,但去除动态特征(及因此导致的多重 classloader)的系统。用 Java 来实现类似系统的可能性,已经在pojosr 项目中得到证明,我们也许会见到一个皆大欢喜的结果。

InfoQ 将持续密切注意 Jigsaw 项目重启的情况,并跟进报道其最新进展。

查看英文原文:Jigsaw, Second Cut

Java语言 & 开发