如果您是库包或框架的作者,你可能希望看到你的库包在大量应用程序中使用。提升库包使用量的一种方法是使其与旧版Java兼容。同时,你可以考虑对库进行模块化,以使其对充分利用Java平台模块系统(JPMS)的应用程序具有吸引力。
但是,JPMS仅由Java 9和更新版本实现。那么,如何在9之前构建一个既模块化又兼容Java版本的库呢?
模块化jar只是一个普通的jar,它包含一个module-info.class代表模块描述符的文件。通常通过编译相应的module-info.java文件来生成该模块描述符。
在Java 8及更早版本上运行时,将忽略模块描述符,因为module-info不是它的合法的Java标识符。这意味着可以通过为目标库的版本创建普通jar并在其中插入模块描述符来生成与Java 9之前版本兼容的模块化jar。
执行这样的办法的标准方法意味着调用java编译器两次:
Maven
Maven 描述 了一种符合上述策略的构建方法。
另一种解决方案是 ModiTect ,一个用于Java 9模块系统的Maven插件。ModiTect提供的目标之一是add-module-info,它允许您将模块描述符添加到当前Maven项目生成的JAR中:
<plugin> <groupId>org.moditect</groupId> <artifactId>moditect-maven-plugin</artifactId> <version>1.0.0.Beta1</version> <executions> <execution> <id>add-module-infos</id> <phase><b>package</b></phase> <goals> <goal>add-module-info</goal> </goals> <configuration> <jvmVersion>11</jvmVersion> <module> <moduleInfoFile> ${basedir}/src/main/java/module-info.java </moduleInfoFile> </module> </configuration> </execution> </executions> </plugin>
ModiTect使用一种聪明的技术来创建模块描述符,而无需Java编译器。它使用 JavaParser 分析module-info.java文件,并使用 ASM 字节码操作框架生成相应的模块描述符。这样,即使您使用Java 9之前的编译器,它也允许您创建模块化jar。
Gradle
从ModiTect的方法中汲取灵感,我实现了一个 Gradle插件, 用于创建面向Java 8或更早版本的模块化jar。
使用这个插件非常简单:build.gradle只需要添加插件并指定库所针对的Java版本:
plugins { id 'java' id 'org.beryx.jar' version '1.0.0' } sourceCompatibility = 1.8 targetCompatibility = 1.8
将module-info.java文件放在src/main/java项目目录中后,您可以使用以下命令构建模块化jar:
./gradlew jar
不需要Java 9+编译器来构建模块化jar,因为该插件使用与ModiTect相同的技术。但是,请记住,此技术无法检查模块描述符的有效性。指向不存在的包,模块,服务或服务实现将不会被发现。
因此,强烈建议通过使用Java 9+编译器和sourceCompatibility与targetCompatibility设置进行额外构建项目来验证模块描述符,好消息是您不需要更改构建脚本来执行此操作。该插件允许您通过设置项目属性来覆盖sourceCompatibility和targetCompatibility值javaCompatibility:
./gradlew -PjavaCompatibility=9 jar
请注意,此项目属性会使用相同的值覆盖两者sourceCompatibility并targetCompatibility
如果插件检测到至少有一个sourceCompatibility并且targetCompatibility有效值> = 9,它会自动应用 Chainsaw 插件,这会增加对JPMS的支持。这就是为什么在Java 9+兼容模式下运行时,构建脚本不需要进行任何更改。
...