通过提供Java 9 module-info.class来了解如何使用Gradle构建支持JPMS( Java平台模块系统 )的Java 6-8库。 
介绍
如果您需要JPMS本身的介绍,请查看此概述 。
这篇文章主要针对Java库维护者。
任何此类维护者都必须选择要针对的JDK:
- 针对最新的JDK( JDK 11或刚刚发布的JDK 12 ),开发人员和用户可以使用新的API和更多功能。
-  但是,这会阻止所有使用较旧JDK的用户使用该库。 - 那些较老的JDK仍然很受欢迎, 在2018年约占95%的份额 ,并预计在2019年将约90%的 份额 。
 
因此,对于许多图书馆维护者而言,后者无疑是一个决定性因素 。 例如, vavr 1.0 旨在以JDK 11为目标 ,但最终将以JDK 8为目标 。
尽管如此,还是建议为JPMS添加一些支持,以期将来会被广泛采用(我想从现在起5年以上)。 Stephen Colebourne在这里描述了三个选项:
- 不执行任何操作(不建议)。
-  最低要求:在MANIFEST.MF文件中添加一个“Automatic-Module-Name条目。
-  最佳:添加一个针对JDK 9+的module-info.class同时提供所有其余针对JDK 6-8 *的类。
在这里,我们将深入研究如何实现选项3(最佳选择)。
 *我写的是JDK 6-8(而不是JDK 5-8),因为在JDK 11中, javac的--release选项限制为6-11。 
理由
不过,在深入研究“如何”之前,让我们先略过“为什么”。
为什么JPMS完全值得打扰? 主要是因为JPMS:
- 提供强大的封装 ,
- 防止引入拆分包 ,
- 确保更快的类加载 。
综上所述,JPMS 非常酷 (更多信息请参见此处 ),鼓励我们采用JPMS是我们的最大利益!
因此,我鼓励Java 6-8库的维护者充分利用JPMS:
-  自己针对模块的JDK 6-8类和其他模块编译module-info.java,
-  对于他们的用户,通过提供module-info.class来使库在module-path上正常工作。
可能的行为
 可以在两个位置找到module-info.java : 
-  与其他所有类,在src/main/java,
-  在单独的“源集”中,例如src/main/java9。
我喜欢选项1,因为它看起来更自然。
 module-info.class可以在两个地方结束: 
- 在根输出目录以及所有其他类中,
-  在META-INF/versions/9(多发行版JAR,又名MRJAR )
阅读了塞德里克·尚波( CédricChampeau)关于MRJAR的帖子后 ,我对MRJAR颇为怀疑,因此我更喜欢选项1。
 但是请注意, Gunnar Morling报告说选项1存在一些问题。另一方面,我希望距JDK 9发行1.5年后,所有主要库都已打补丁以正确处理module-info.class 。 
每个构建工具的示例库
 本节包含一些在针对JDK 6-8时提供module-info.class的库示例。 
蚂蚁
-  Lombok (JDK 6 main + JDK 9 module-info.class)
马文
-  ThreeTen-extra (JDK 8 main + JDK 9 module-info.class)
-  Google Gson –尚未发布(JDK 6 main + JDK 9 module-info.class)
-  SLF4J –尚未发布( META-INF/versions/9JDK 6 main + JDK 9module-info.class)
请注意, Maven编译器插件提供了如何提供这种支持的示例 。
摇篮
我还没有找到任何流行的库使用Gradle提供这种支持(请注释,如果您知道的话)。 我只知道vavr试图这样做( #2230 )。
Gradle中的现有方法
修改
 ModiTect (由Gunnar Morling编写 )及其Gradle插件 (由Serban Iordache编写 )具有一些非常酷的功能 。 本质上,ModiTect基于特殊符号或直接从module-info.java 生成 module-info.class -info.class, 而无需使用javac 。 
 但是,在从module-info.java直接生成的情况下,ModiTect有效地复制了javac所做的操作,同时引入了自己的问题(例如#90 )。 这就是为什么我觉得它不是最好的工具。 
Badass Jar插件
Serban Iordache还创建了一个Gradle插件 ,该插件可以“无缝创建针对9之前的Java版本的模块化jar”。
看起来不错,但是:
-  为了构建适当的JAR并验证module-info.java,Gradle构建必须运行两次,
-  它不使用javac的--release选项,该选项保证仅引用正确的API,
-  它不使用javac来编译module-info.java。
同样,我觉得这里不是正确的工具。
JpmsGradlePlugin
这是我最近的发现: JpmsGradlePlugin由阿克塞尔Howind 。
 该插件可以做一些不错的事情(例如,从javadoc任务中排除module-info.java ),但是: 
-  它也不使用javac的--release选项,
- 它不完全支持Java模块化(例如,模块修补),
-  感觉还不够成熟( 难以遵循的代码,非标准行为,例如直接调用javac)。
Gradle中的拟议方法
摇篮脚本
最初,我想通过添加自定义源集来做到这一点 。 但是,事实证明,这种方法会引入不必要的配置和任务 ,而我们真正需要的只是一个额外的任务,它正确地“钩住”了构建生命周期 。
结果,我想到了以下几点:
-  将compileJava配置为:-  排除 module-info.java,
-  使用--release 6/7/8选项。
 
-  排除 
-  添加一个名为compileModuleInfoJava的新JavaCompile任务,并将compileModuleInfoJava配置为:-  仅包含 module-info.java,
-  使用--release 9选项,
-  使用compileJava的类路径作为--module-path* ,
-  使用compileJava*的目标目录 ,
-  取决于 compileJava* 。
 
 
-  仅包含 
-  配置classes任务以依赖于compileModuleInfoJava。
以上内容在Groovy DSL中表示为Gradle脚本,可以在我的Stack Overflow答案中找到。
 *这三个步骤对于compileModuleInfoJava查看由compileJava编译的类是compileJava 。 否则,由于未解析的引用, javac将无法编译module-info.java 。 请注意,在这种配置中,每个类仅被编译一次 (与推荐的Maven Compiler Plugin配置不同 )。 
不幸的是,这样的配置:
- 在存储库之间不容易重用,
- 不完全支持Java模块化。
Gradle模块插件
最后,有一个插件( Gradle Modules Plugin ),它为Gradle添加了对JPMS的完全支持(由Java 9 Modularity的作者Sander Mak和Paul Bekker创建 )。
该插件仅不支持本文所述的方案。 因此,我决定:
- 通过此插件提交功能请求: #72
- 提供具有#72的完整实现的“拉取请求”(作为“概念证明”): #73
我尽力做出这些高质量的贡献。 最初的反馈意见非常受欢迎 (甚至Mark Reinhold都喜欢!)。 谢谢!
现在,我正在耐心地等待进一步的反馈 (以及潜在的改进要求),然后才能(希望)合并PR。
摘要
 在本文中,我展示了如何使用Gradle构建Java 6-8库,以便将module-info.java编译为JDK 9格式(JPMS支持),而将所有其他类编译为JDK 6-8格式。 
我还建议对这种配置使用Gradle Modules插件 (一旦我的PR合并并且发布了新的插件版本)。
翻译自: https://www.javacodegeeks.com/2019/03/building-java-6-8-libraries-jpms-gradle.html