通过学习如何从命令行创建、编译和执行单模块和多模块项目,让您了解Java9的模块化。
(在本文中,我们将介绍Java平台模块系统(JPMS),这是Java9版本中最大的变化。在本文中,我们将看一看JPMS的一些基础知识(为什么我们需要模块?JDK中发生了什么变化?)。之后,我们将了解如何创建、编译和执行单个模块的应用程序。最后,我们将了解如何创建、编译和执行多模块应用程序。在本文中,我们将只使用命令行工具。使用的示例可以在GitHub上找到。
为什么需要模块化?
让我们从一个基本问题开始:为什么我们还需要模块?为了回答这个问题,我们来看看JSR-376。以下项目符号是从JSR复制的:
1. 可靠的配置—开发人员长期以来一直受到用于配置程序组件的脆弱、易出错的类路径机制的影响。类路径不能表示组件之间的关系,因此如果缺少必需的组件,则在尝试使用它之前,不会发现该组件。类路径还允许从不同组件加载同一包中的类,从而导致不可预测的行为和难以诊断的错误。建议的规范将允许组件声明它依赖于其他组件,就像其他组件依赖于它一样。
2. 强封装—Java编程语言和Java虚拟机的访问控制机制无法让组件阻止其他组件访问其内部包。建议的规范将允许组件声明其包中哪些可以被其他组件访问,哪些不能。
3. 一个可扩展的平台——Java SE平台的规模不断扩大,使得在小型设备中使用变得越来越困难,尽管事实上许多这样的设备能够运行SE类Java虚拟机。JavaSE8(JSR337)中引入的紧凑概要文件在这方面有所帮助,但它们还不够灵活。建议的规范将允许javase平台及其实现被分解成一组组件,开发人员可以将这些组件组装成只包含应用程序实际需要的功能的定制配置。
4. 更大的平台完整性—随意使用JavaSE平台实现内部的API既是安全风险,也是维护负担。所提议的规范提供的强封装将允许实现javase平台的组件阻止对其内部api的访问。
5. 提高了性能—当知道一个类只能引用其他一些特定组件中的类而不是运行时加载的任何类时,许多提前的、全程序优化技术可以更有效。当应用程序的组件可以与实现javase平台的组件一起优化时,性能会得到特别的提高。
模块:一些基础知识
Java模块
为了查看Java中有哪些模块可用,我们可以输入以下命令:
java --list-modules
将显示一个包含可用模块的列表。以下是列表摘录:
java.activation@9
java.base@9
java.compiler@9
...
从列表中,我们可以看到这些模块分为四类:以java(标准java模块)开头的模块、以javafx(javafx模块)开头的模块、以jdk(jdk特定模块)开头的模块和以oracle(oracle特定模块)开头的模块。每个模块以@9结尾,表示该模块属于Java9。
模块声明
模块属性位于module-info.java文件。为了查看此文件中定义的模块的描述,可以使用以下命令:
java --describe-module java.sql
这将输出以下内容:
java.sql@9
exports java.sql
exports javax.sql
exports javax.transaction.xa
requires java.logging transitive
requires java.xml transitive
requires java.base mandated
uses java.sql.Driver
exports
关键字表示这些包可用于其他模块。这意味着,公共类在默认情况下仅在模块中是公共的,除非在模块信息声明中指定了它。
requires
关键字表示此模块依赖于另一个模块。
uses
关键字表示此模块使用服务。
Javadoc
还可以看看java9 javadoc。正如您将看到的,它现在被划分为模块而不是包。在模块的Javadoc中,可以找到模块图和包。实际上,与上面相同的结构可以在--descripe模块中找到。
创建自己的JRE
JDK的目录结构发生了一些变化。添加了一个目录jmods。此目录包含每个模块的jmod文件。jmod文件类似于JAR文件,但用于模块。您可以解压缩文件并查看内容。它包含此模块的已编译类。
在我的笔记本电脑上,整个JDK的大小是482MB。在使用jlink时,我们可以只使用应用程序中使用的模块创建自己的JRE。下面的命令将创建一个只包含java.base文件模块。
jlink --module-path ../jmods --add-modules java.base --output d:\java\jre
这个运行时的大小只有36MB。很简单,不是吗?
Hello Modules!
现在是采取实际行动的时候了!
示例说明
在接下来的部分中,我们将使用Hello模块示例。将使用以下目录结构:
mods
src
|_ com.mydeveloperplanet.jpmshello - module-info.java
|_ com
|_mydeveloperplanet
|_ jpmshello - HelloModules.java
target
mods目录将用于编译的模块和类。
src目录将用于我们的源代码。在这个目录中,我们首先有一个目录com.mydeveloperplanet.jpmshello
. 这是我们模块的顶级目录。它使用与包相同的命名约定。在这个目录中,一个名为module的module-info.java存在。内容将在后面显示和解释。
在module目录中,我们已经熟悉了包结构。我们有一个HelloModules.java类。目标目录将用于存储JAR文件。
下面是HelloModules.java显示类。这是一个简单的HelloModules示例:一个主类,它驻留在包中com.mydeveloperplanet.jpmshello
.
package com.mydeveloperplanet.jpmshello;
public class HelloModules {
public static void main(String[] args) {
System.out.println("Hello Modules!");
}
}
模块的module-info.java具体如下。
module com.mydeveloperplanet.jpmshello {
requires java.base;
}
在关键字module
之后,设置我们模块的名称。在括号内,我们定义了在模块中使用的标准Java模块。在我们的例子中,我们只使用java.base
文件模块。我们使用关键字requires
来定义用法。无需显式添加java.base
文件模块,因为它总是隐式添加的。但在我们的示例中,为了清楚起见,我们还是添加了它。
编译模块
下一步是编译类。在这篇文章中,我们将使用命令行来编译和执行应用程序—这总是一件好事。这样,你就能更好地理解事情是如何运作的。稍后,我们将了解IDE支持以及使用Maven时它是如何工作的。
使用以下命令,我们编译应用程序。d选项指定编译类的目标目录。在我们的例子中,我们把它编译成一个子目录,com.mydeveloperplanet.jpms
你好,类似于我们的模块。最后,我们指定要编译的类。
javac -d mods/com.mydeveloperplanet.jpmshello src/com.mydeveloperplanet.jpmshello/module-info.java src/com.mydeveloperplanet.jpmshello/com/mydeveloperplanet/jpmshello/HelloModules.java
执行
要执行编译的类,我们需要设置模块路径。此选项设置可以在其中找到模块的目录。module选项设置必须调用的主类。需要设置模块和包以及主类所在的类。
java --module-path mods --module com.mydeveloperplanet.jpmshello/com.mydeveloperplanet.jpmshello.HelloModules
输出为:
Hello Modules!
创建JAR文件
上面的示例已经很酷了,但是我们还希望将应用程序打包到一个JAR文件中,以便能够轻松地分发它。我们使用下面的命令创建JAR文件(前提是目标目录存在)。file选项指定JAR文件的位置和名称。main class选项指定应用程序的入口点——换句话说,就是主类所在的位置。C选项指定要包含在JAR文件中的类的位置:
jar --create --file target/jpms-hello-modules.jar --main-class com.mydeveloperplanet.jpmshello.HelloModules -C mods/com.mydeveloperplanet.jpmshello.
现在我们可以从JAR文件执行应用程序:
java --module-path target/jpms-hello-modules.jar --module com.mydeveloperplanet.jpmshello/com.mydeveloperplanet.jpmshello.HelloModules
输出同样是:
Hello Modules!
我们还可以检查模块描述,就像我们之前对Java标准模块所做的那样:
java --module-path target/jpms-hello-modules.jar --describe-module com.mydeveloperplanet.jpmshello
这将按预期输出以下内容:
requires java.base
contains com.mydeveloperplanet.jpmshello
在第一节中,我们创建了一个只包含java.base
文件模块。现在使用这个定制的JRE运行JAR文件。正如您将看到的,输出再次打印HelloModules!
从其他模块导入包
我们将通过使用模块中的包而不是java.base文件. 我们将使用java.xml.XMLConstants.XML_NS_PREFIX
前缀并将其打印到控制台。
这个HelloModules.java类变成如下。
package com.mydeveloperplanet.jpmshello;
import static javax.xml.XMLConstants.XML_NS_PREFIX;
public class HelloModules {
public static void main(String[] args) {
System.out.println("Hello Modules!");
System.out.println("The XML namespace prefix is: " + XML_NS_PREFIX);
}
}
像以前一样编译它。我们没有导入javax.xml
文件模块,因此编译可能会失败。这样做时,import
语句会出现以下错误:
error: static import only from classes and interfaces
当然,我们知道如何解决这个问题-我们必须在module-info.java我们需要java.xml
文件模块。它变成如下:
module com.mydeveloperplanet.jpmshello {
requires java.base;
requires java.xml;
}
现在编译、创建JAR文件并运行应用程序。输出为:
Hello Modules!
The XML namespace prefix is: xml
创建多模块应用程序
让我们更进一步。我们将添加另一个模块,com.developerplanet.himodule
模块,并使用主模块中另一个模块的类HiModules。目录结构变为:
mods
src
|_ com.mydeveloperplanet.jpmshello - module-info.java
| |_ com
| |_mydeveloperplanet
| |_ jpmshello - HelloModules.java
|_ com.mydeveloperplanet.jpmshi - module-info.java
|_ com
|_ mydeveloperplanet
|_ jpmshi - HiModules.java
target
内容HiModules.java文件是:
package com.mydeveloperplanet.jpmshi;
public class HiModules {
public String getHi() {
return "Hi Modules!";
}
}
模块module-info.java具体如下:
module com.mydeveloperplanet.jpmshi {
}
我们扩展我们的HelloModules.java类以使用HiModules
类。
package com.mydeveloperplanet.jpmshello;
import com.mydeveloperplanet.jpmshi.HiModules;
import static javax.xml.XMLConstants.XML_NS_PREFIX;
public class HelloModules {
public static void main(String[] args) {
System.out.println("Hello Modules!");
System.out.println("The XML namespace prefix is: " + XML_NS_PREFIX);
HiModules hiModules = new HiModules();
System.out.println(hiModules.getHi());
}
}
编译多模块项目
我们编写了多模块项目。与单模块项目相比,我们修改编译命令如下:
- 模块输出路径(-d选项)更改为mods
- 我们添加模块源路径选项,以指定模块源路径的位置
- 我们添加了模块的源代码
com.mydeveloperplanet.jpmshi
要编译的类的列表
javac -d mods --module-source-path src src/com.mydeveloperplanet.jpmshello/module-info.java src/com.mydeveloperplanet.jpmshello/com/mydeveloperplanet/jpmshello/HelloModules.java src/com.mydeveloperplanet.jpmshi/module-info.java src/com.mydeveloperplanet.jpmshi/com/mydeveloperplanet/jpmshi/HiModules.java
但编译失败,出现以下错误:
error: package com.mydeveloperplanet.jpmshi is not visible
我们忘了两件事:
我们需要告诉模块com.mydeveloperplanet.jpmshello
它需要模块com.mydeveloperplanet.jpmshi
我们需要告诉模块com.mydeveloperplanet.jpmshi
那个包裹com.mydeveloperplanet.jpmshi
已导出,因此可供其他模块使用
模块-信息.java从模块com.mydeveloperplanet.jpmshello
变成:
module com.mydeveloperplanet.jpmshello {
requires java.base;
requires java.xml;
requires com.mydeveloperplanet.jpmshi;
}
module-info.java从模块com.mydeveloperplanet.jpmshi
变成:
module com.mydeveloperplanet.jpmshi {
exports com.mydeveloperplanet.jpmshi;
}
在这些更改之后,编译成功,编译的类可以在mods目录中找到。
为多模块项目创建JAR文件
现在是创建JAR文件的时候了。对于每个模块,我们需要创建一个JAR文件。
为模块创建JAR文件com.mydeveloperplanet.jpmshi
:
jar --create --file target/jpms-hi-modules.jar -C mods/com.mydeveloperplanet.jpmshi
为模块创建JAR文件com.mydeveloperplanet.jpmshello
. 这与我们用于为单模块项目创建JAR文件的命令相同:
jar --create --file target/jpms-hello-modules.jar --main-class com.mydeveloperplanet.jpmshello.HelloModules -C mods/com.mydeveloperplanet.jpmshello
执行多模块项目
现在运行应用程序:
java --module-path target --module com.mydeveloperplanet.jpmshello/com.mydeveloperplanet.jpmshello.HelloModules
输出为:
Hello Modules!
The XML namespace prefix is: xml
Hi Modules!
使用自定义JRE运行此示例失败,错误如下:
Error occurred during initialization of boot layer
java.lang.module.FindException: Module java.xml not found, required by com.mydeveloperplanet.jpmshello
这是我们所能预料到的,因为定制的JRE没有包含这个模块java.xml
文件.
让我们用模块创建自定义JREjava.xml
文件包括:
jlink --module-path ../jmods --add-modules java.base,java.xml --output d:\java\jre
使用自定义JRE再次运行该示例,现在它成功运行了!
摘要
在本文中,我们在Java平台模块系统中迈出了第一步。我们看了一些理论,然后创建、编译并执行了一个单模块应用程序。最后,我们创建、编译并执行了一个多模块应用程序。我们还介绍了该模块的一些基本关键字module-info.java类。
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1359.html
暂无评论