要使用Java9模块系统,首先需要理解它。在本教程中,我将向您介绍:
- 模块的基本定义、内容和配置
- Java9中封装的工作原理
- 如何定义接口
- 如何列出可用模块
- java8和java9应用程序的比较
- 如何使用模块的基本规则
- 类路径和模块路径的区别
现在,让我们来认识一下Java语言的新一等公民:Module。
定义Java9模块
模块是代码、数据和资源的集合。它是一组相关的包和类型(类、抽象类、接口等),包含代码、数据文件和一些静态资源。
例如,模块描述符module-info.java是Java9模块中的资源之一。(模块描述符是模块声明的编译版本。创建此文件时,您必须知道两位信息:模块将依赖于什么以及它将导出什么。)
每个模块只包含一组相关的代码和数据,以支持单一责任原则(SRP):“一个类改变的原因不应超过一个。”(其思想是设计一个具有一个责任的类。)更简单的说法是:模块=代码+数据。
Java9模块系统的主要目标是支持Java中的模块化编程。
因此,到目前为止,Java拥有许多一流的公民,这些语言属性:
- javase1.0中OOP(支持基本面向对象编程)中的包和对象;引入包来组织Java类型
- JavaSE8中FP(支持函数式编程)中的Lambda表达式
- JavaSE9中模块系统(支持模块化编程)的模块;相关的包被分组在模块下,模块取代包成为基本的重用单元
现在,让我们研究基本模块配置和依赖关系,然后讨论增强封装的模块机制。
基本模块和可靠配置
目前,java9模块系统有大约98个模块,但它仍在继续发展。Oracle将jdk jar和javase规范分为两组模块。
所有JDK和用户定义模块的默认模块都是基本模块java.base
文件. 它是一个独立的模块,不依赖于任何其他模块。java.base
文件被称为“Java9模块之母”
在下面的图中,您可以看到系统的模块化方面,并且可能会有机会理解模块化的JDK意味着您也可以模块化自己的应用程序。
这只是98个平台模块中的一小部分。
该图是一个依赖关系图。每个框表示一个模块,如果一个模块有一个指向另一个模块的箭头,则表示该箭头所指向的模块需要它所指向的模块来执行其功能。
正如你在图片中看到的,java.base
文件是其他模块所依赖的模块。这是因为它包含Java对象、整数、字符串等基本类。没有这些类,代码就不能工作。
我可以使依赖关系更加明确:
这个关系图变得非常混乱,所以通常您会忽略模块到应用程序的依赖关系java.base
文件. 但重要的是要记住java.base
文件总是在那里。
在下图中java.logging
文件以及java.xml
文件模块在图中仍然有一个明确的箭头。这表明java.base
文件是模块的唯一依赖项,对于java.logging
文件以及java.xml
文件.
您可以看到一个模块具有除java.base
文件
这个java.sql语言模块使用java.logging
文件模块(这是有意义的,因为它可能有一些内部日志记录要做)和java.xml
文件模块(这可能有点奇怪,但它是用来处理某些数据库的XML功能的)。
通过检查此图中的模块及其显式依赖关系,您可能已经比查看rt.jar像在以前版本的语言中一样归档。
您可以清楚地了解不同的功能是如何打包在JDK中的,并且很明显提供功能的不同模块之间的依赖关系存在于何处。
将这些信息显式地编码到模块(带有依赖项)中,可以更容易地创建可靠的应用程序,尤其是在应用程序开发中应用这些相同的原则时。您的新的、更模块化的应用程序将与此图一样易于理解。
实现更好的封装和定义良好的接口
拥有明确的依赖关系是模块化的主要基础之一,但是您还需要一种更强大的封装形式和定义良好的接口,因此让我们放大单个模块,看看JPMS如何处理这些问题。
你看到的是一个模块,母模块java.base
文件,如您所见,它有两个部分:
上半部分列出了包。在这种情况下,java.lang
, java.util
, java.io
(但现实中还有很多java.base
文件模块)。这些包都是这个模块的公共接口的一部分。每个模块取决于java.base
文件你可以看到这些包裹里的所有东西。
在模块屏蔽部分的行下方,您可以看到不同的包,其名称如下sun.util
以及jdk.internal
. 这是一个信号,表明这些包是内部实现细节和依赖于它们的其他模块java.base
文件将无法访问这些包中的任何内容。
这是模块系统提供的强封装的一个例子,这是一个关键的安全元素。
列出JDK的模块
java9的一个重要组织方面是将JDK划分为模块,以支持jep200中概述的各种配置。要列出模块,可以使用JDK的bin文件夹中的java命令和--list-modules
选项:
java --list-modules
java --list-modules | grep -e 'java\'.''
java --list-modules | grep "java\."
jdk9的一组模块包括:
- 实现JavaSE规范的标准模块(名称以Java.*开头)
- JavaFX模块(名称以JavaFX.*开头)
- JDK特定模块(名称以JDK.*开头)
- Oracle特定模块(名称以Oracle.*开头)
每个模块名称后面都有一个版本字符串。在本例中,我使用的是jdk9.0.4版本,因此每个模块后面都有版本字符串@9.0.4。
另外,这个列表可以帮助您通过跟踪JEPs和JSR中Java模块化的发展,来研究将模块化引入Java 9的决策:
- JEP 200: The Modular JDK
- JEP 201: Modular Source Code
- JEP 220: Modular Run-Time Images
- JEP 260: Encapsulate Most Internal APIs
- JEP 261: Module System
- JEP 275: Modular Java Application Packaging
- JEP 282: Jlink: The Java Linker
- JSR 376: Java Platform module System
- JSR 379: Java SE 9
现在让我们比较一下Java8和Java9应用程序。
比较Java8和Java9应用程序
您已经使用版本5、6、7和8开发了许多Java应用程序,因此您可能非常了解9之前的Java应用程序的外观及其包含的组件。对于那些需要复习的人,Java SE 8应用程序:
以及Java9应用程序:
在Java8和更早的应用程序中,顶级组件是包package。它将一组相关类型放入一个组中。它还包含一组资源。
java9应用程序与java8没有太大区别;它引入了一个新组件module,用于将一组相关的包放入一个组中。同时还介绍了另一个新组件:模块描述符module-info.java
Java8应用程序将包作为顶级组件,Java9应用程序将模块作为顶级组件。
顺便说一下,每个Java9模块只能是一个具有一个模块描述符的模块。与Java8包不同,您不能将多个模块构建到单个模块中。
下面列出了Java 9模块中的主要组件:
- 一个模块
- 模块名称
- 模块描述符
- 成套设备
- 类型和资源集
资源可以是模块描述符或任何其他属性或XML。
接下来,让我们深入研究模块和模块描述符。
模块和模块描述符基础
现在我们将讨论模块和模块描述符基础的两个更重要的概念:语法和规则。
模块基础知识和规则
在开发任何Java 9模块时,应记住以下重要的基本规则:
- 每个模块都有一个唯一的名称
- 每个模块在源文件中都有一些描述
- 模块描述符文件放在顶层目录中
- 每个模块可以有任意数量的包和类型
- 一个模块可以依赖于任意数量的模块
每个模块都有一个唯一的名称
因为模块位于JVM的全局空间中,所以每个模块都应该有一个唯一的名称。与包和JAR文件名一样,可以使用反向域名模式来定义模块名。
例如,如果您要为http://www.taman.com.eg域名开发模块,则可以使用例如:eg.com.taman.mod1
作为第一个模块名,例如:eg.com.taman.mod2
作为第二个模块名,依此类推。
每个模块在源文件中都有一些描述
模块描述在名为Module的源文件中表示module-info.java应该这样命名。每个模块应该只有一个模块描述符(module-info.java).
模块描述符是一个Java文件。它不是XML、文本或属性文件。
模块描述符文件放在顶层目录中
顶层目录是模块的根文件夹。
例如,如果您要开发例如:eg.com.taman.mod1
模块,则应将模块描述符放在例如:eg.com.taman.mod1
模块目录。
每个模块可以有任意数量的包和类型
一个模块可以依赖于任意数量的模块。
现在,让我们看看模块描述符中有什么。
模块描述符
在Java9模块中,模块描述符是包含描述模块的模块元数据的资源。它不是XML或属性文件,而是普通的Java文件。
必须将此文件命名为module-info.java并将其放在模块的根文件夹中。与其他Java源文件一样,模块文件被编译到模块中module-info类使用javac
命令。
使用module关键字创建模块描述符:
module {
// Module Meta Data goes here.
}
例如:
module eg.com.taman.mod1 {
}
这是一个简单而最小的模块描述符示例。让我们讨论一下模块元数据。
模块元数据
模块包含以下基本元数据:
- 唯一的名字
- 出口条款
- requires子句
我将在下面的部分更深入地讨论并提供一些示例。
唯一的名字
模块具有唯一的名称。使用module关键字定义模块类型,如本例所示:
module eg.com.taman.mod1 {
}
出口条款
模块可以将其包导出到外部世界,以便其他模块可以使用它们。在模块描述符中,使用exports
子句将包导出到外部世界或其他模块:
module eg.com.taman.mod1 {
exports eg.com.taman.service;
}
请注意,并非强制导出所有包。由你决定导出哪一种。
requires子句
模块可以导入或使用其他模块包。在模块描述符中,使用requires子句导入其他模块以使用其包:
module eg.com.taman.mod2 {
requires eg.com.taman.mod1;
}
正如你在这个例子中看到的,eg.com.taman.mod1
已导出eg.com.taman.service
,所以eg.com.taman.mod2
要求mod1导入其导出的所有包,以便在其子类型(类、枚举、接口等)中使用它们。
但是请记住,exports
关键字将包导出到其他模块,并且需要关键字imports
模块才能在内部使用所有导出的包。在模块中定义但未导出的任何包都是私有封装的,永远无法访问。
一个模块可以有超过这个数量的元数据,但这足以让您开始模块化编程。
现在,让我们确定一些关于模块描述符语法的要点。
关于模块描述符的注意事项
在构建模块描述符之前,您应该记住以下要点:
- 模块描述符可以只包含模块名而不包含其他内容;不包含导出或requires子句。
- 模块描述符可以由一个或多个exports子句组成,而不包含requires子句;这意味着它将包导出到其他模块,但不依赖于任何其他模块—它是一个独立的模块。
- 模块描述符可以同时具有exports和requires子句;这意味着它将包导出到其他模块并使用其他模块的包—因为它依赖于其他模块,所以它不是一个独立的模块。
- 模块描述符可以有零个、一个或多个requires子句。
模块是从modulepath加载的(就像类是从类路径加载的一样)。
等等,你说。为什么我不能像以前那样使用类路径?
为什么选择modulepath?
作为一名Java开发人员,您知道什么是类路径地狱:与Windows®编程中的DLL地狱类似,Java中的类路径地狱之所以出现,是因为您的程序不是一组固定的代码,而是JVM在特定实例中加载的一组类。您的代码可能处于这样一种情况:由于解析规则,平台上的同一命令行会导致不同的行为。目录结构可能不同。标准库的版本可以不同,也可以隐藏。因为Java支持第一次遇到的策略,所以未知的排序依赖关系可能会使代码变得混乱。
从Java9开始,您将跳入另一种地狱:modulepath地狱。
类路径是用户定义并内置的一系列类和包或jar。JVM或Java编译器需要类路径来编译应用程序或类。
在Java9之前,编译器和运行时通过类路径定位类型:包含已编译Java类的文件夹和库存档文件的列表。它是由CLASSPATH环境变量、放在JRE特殊文件夹中的扩展以及提供给javac和java命令的选项的组合定义的。目标是减少应用程序启动时间。
因为类型可以从几个不同的位置加载,所以搜索这些位置的顺序会导致应用程序变得脆弱。
很多年前,我在我的系统上安装了一个来自第三方供应商的Java应用程序。该应用程序的安装程序将一个旧版本的Java库放入JRE的extensions文件夹中。我系统上的几个Java应用程序依赖于该库的更新版本,因为它包含了库的旧类型的附加类型和增强版本。
因为JRE的extensions文件夹中的类是在类路径上的其他类之前加载的,所以依赖于较新库版本的应用程序停止工作,在运行时出现NoClassDefFoundErrors
和NoSuchMethodErrors
失败,有时是在应用程序开始执行很久之后。
modulepath是一系列模块(以文件夹或JAR格式提供)。如果模块是文件夹格式,则表示该模块是分解模块格式。如果它是JAR格式的,那么这个JAR就是模块化JAR。
模块和模块描述符提供的可靠配置有助于消除许多此类运行时类路径问题。每个模块都显式地声明其依赖项,这些依赖项作为应用程序启动来解析。
一个模块,一个包
modulepath只能包含每个模块中的一个,并且每个包只能在一个模块中定义。如果两个或多个模块具有相同的名称或导出相同的包,则运行时将在运行程序之前立即终止。
总结
在本教程中,讲解了模块的基本定义、内容和配置;Java 9中封装的工作原理和接口的定义;如何列出可用的模块;Java 8和Java 9应用程序之间的区别;如何使用模块的基本规则;以及classpath和modulepath之间的区别(以及他们能制造的地狱)。