Java平台模块系统- 与已有代码工作

转自

Project Jigsaw

warn: requires transitive directive for an automatic module

如果在Java 9上开发全新的应用,那么上面介绍的模块声明文件中的概念就已经足够了。不过在很多情况下,我们需要兼容已有的应用。这个时候就需要了解本节中介绍的未命名模块(unnamed module)和自动模块(automatic module)的概念。

未命名模块

从上面的小节中我们可以看到,Java 9对如何在不同的模块之间访问Java类型有严格的限制。如果你从零开始开发一个Java9上的新应用,那么你应该使用新的模块系统。Java 9仍然提供了良好的向后兼容性,可以运行所有在Java 9之前编写的应用程序。这是通过未命名模块来实现的。

如果模块系统需要加载一个来自不在任何模块所声明导出的包中的Java类型时,它会尝试从CLASSPATH中加载。如果该Java类型被成功加载,那么该类型被视为是一个特殊模块的成员。该特殊的模块称为未命名模块。未命名模块的特殊性在于它读取所有其他的模块,并且导出所有内部包含的包。

如果一个类型是从CLASSPATH中加载的,那么作为未命名模块中的成员,它可以访问所有其他模块中所导出的包,也包括Java平台内部的模块。正因为这样,Java 9之前编写的应用,可以不经过任何改动就运行在Java 9之上。虽然未命名模块导出了所有内部的包,但是其它命名模块中的代码并不能访问未命名模块中的类型。我们也没办法通过requires来声明对未命名模块的依赖关系。这样的限制是必须的,否则我们就失去了引入模块系统的所有好处,又重新回到了依靠CLASSPATH的老路上去了。未命名模块的主要目的是保持向后兼容性。如果一个包同时在某个命名模块和未命名模块中出现,那么在未命名模块中的包会被忽略。所以在CLASSPATH中的包不会干扰在命名模块中的代码。

自动模块

由于Java 9是向后兼容的,对于已有的应用来说,并不一定要升级到使用模块。不过还是建议升级来利用模块系统的好处。

推荐的升级方式是采用自底向下的做法,也就是说从依赖关系树中的叶子节点开始做起。举例来说,如果一个应用有3个子模块或子项目,A、B和C。它们之间的依赖关系是A -> B -> C。当升级该应用到Java 9时,推荐的做法是从C开始,然后再依次是B和A。这样的话,当C被升级为模块之后,A和B都还在未命名模块中,可以继续访问模块C中的类型。这是因为我们之前提到的,未命名模块可以读取所有的其它命名模块。然后我们升级B为一个命名模块,并声明其依赖模块C。最后再把A升级为模块,就完成了该项目的升级。

然而这种自底向上的升级方法并不总是可行的。有些库可能是第三方所维护的,我们没有办法控制这些库升级到模块的时间。我们仍然希望可以升级那些依赖这些第三方库的代码。我们没有办法直接把应用本身的代码升级为模块,而把第三方库放在CLASSPATH中。这样的话,这些第三方库会出现在未命名模块中。而我们之前已经说过,命名模块是不能访问未命名模块中的Java类型的。为了解决这个问题,Java模块系统中有另外一个自动模块的概念。我们只需要把这些第三方库放在模块路径中,它们会被转换成自动模块。

与其他显式创建的命名模块不同的是,自动模块是从普通的JAR文件中自动创建出来的。这些JAR文件中并没有包含模块声明文件module-info.class。自动模块的名称来自于JAR文件的清单文件MANIFEST.MF中的属性Automatic-Module-Name,或从JAR文件的名称中自动推断出来的。其他模块可以使用该名称来声明对该自动模块的依赖。推荐的做法是使用清单文件的属性Automatic-Module-Name,比依赖JAR文件名称的做法要更加可靠。

自动模块的特殊性体现在下面几个方面:

  • 自动模块读取所有其它的命名模块。
  • 自动模块导出所包含的全部包。
  • 自动模块读取未命名模块。
  • 自动模块对其它自动模块是传递可读的。

自动模块是CLASSPATH和命名模块之间的桥梁。最终的目的当然是把Java 9之前的那些子模块、子项目和库都升级到Java 9的命名模块。但是在升级过程中,我们可以把这些子模块、子项目和库的JAR文件加入到模块路径中作为自动模块来使用。

在下面的代码中,模块D中的类dtest.MyD使用了Google Guava库中的com.google.common.collect.Lists

package dtest;
   
import com.google.common.collect.Lists;
   
public class MyD {
 public static void main(String[] args) {
   System.out.println(Lists.newArrayList("Hello", "World"));
}
}

Guava还没有升级为Java 9的模块。在模块D的描述文件中,我们可以使用requires guava来声明对Guava的依赖关系。guava是Guava库对应的自动模块的名称,从JAR文件名推断而来。

module D {
 requires guava;
}

在编译类MyD时,我们需要把Guava的JAR文件guava-19.0.jar添加到模块路径中。这是通过javac命令的新参数--module-path来设置的。此处我们把JAR文件guava-19.0.jar放在了~/libs目录中。

$ javac --module-path ~/libs<src_path>/*.java <src_path>/dtest/*.java

注:虽未深搜,但当下不少demo都是传统的Java项目,并未见到基于Spring Boot的Web项目。当下eladmin项目已完成主体的升级,但启动方面存在问题,无法启动,亟待解决。

Leave a Reply

Your email address will not be published. Required fields are marked *

lWoHvYe 无悔,专一