Java Top.

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>查看课程

1.概述

Java 9在包之上引入了一个新的抽象级别,正式称为Java平台模块系统(JPMS),或简称为“模块”。

在本教程中,我们将通过新系统并讨论其各个方面。

我们还将构建一个简单的项目来展示我们在本指南中学习的所有概念。

2.模块是什么?

首先,我们需要了解一个模块在我们能够理解如何使用它们之前。

模块是一组紧密相关的包和资源以及一个新的模块描述符文件。

换句话说,它是一个“java包包”抽象,允许我们使我们的代码更加可重复使用。

2.1。包

模块内的包与我们已经使用的Java包相同,我们已经使用了Java。

当我们创建一个模块时,我们就像我们以前与任何其他项目一样一样,我们在内部组织代码。

除了组织我们的代码,套餐用于确定模块外部可公开访问的代码。我们将花更多的时间在文章后面谈论这一点。金宝搏官网188be

2.2。资源

每个模块都负责其资源,如媒体或配置文件。

以前我们将所有资源放入我们项目的根级别,并手动管理属于应用程序不同部分的资源。

使用模块,我们可以使用需要它的模块运送所需的图像和XML文件,使我们的项目更容易管理。

2.3。模块描述符

创建模块时,我们包含一个描述符文件,该文件定义了我们新模块的多个方面:

  • 名称- 我们的模块的名称
  • 依赖性- 此模块取决于其他模块的列表
  • 公共包裹- 我们希望从模块外部访问的所有包的列表
  • 提供的服务-我们可以提供其他模块可以使用的服务实现
  • 服务消耗—允许当前模块成为服务的消费者
  • 反射权限-显式允许其他类使用反射来访问包的私有成员

模块命名规则类似于我们的名称包(允许点,破折号不是)。做项目风格(My.module)或Reverse-DNS是非常常见的(com.金宝搏188体育baeldung.mymodule)风格的名字。我们将在本指南中使用项目风格。

我们需要列出所有想要公开的包,因为默认情况下所有包都是模块私有的。

反射也是如此。默认情况下,我们无法在我们导入另一个模块的类上使用思考。

稍后在文章中,我们将查看如何使用模块描述符文件的示例。

2.4。模块类型

新模块系统中有四种类型的模块:

  • 系统模块- 这些是我们运行时所列出的模块列表模块上面的命令。它们包括Java SE和JDK模块。
  • 应用程序模块- 当我们决定使用模块时,这些模块是我们通常想要构建的。它们在编译中命名和定义module-info.class包含在组装罐中的文件。
  • 自动模块- 我们可以通过将现有jar文件添加到模块路径来包括非官方模块。模块的名称将从JAR的名称派生。自动模块将对路径加载的每个其他模块都有完全读取的访问权限。
  • 未命名的模块- 当类或jar加载到类路径上时,但不是模块路径时,它会自动添加到未命名的模块。它是一个捕获的所有模块,以与先前写的Java代码保持向后兼容性。

2.5。分配

模块可以分发两种方式之一:作为JAR文件或作为“爆炸”编译的项目。当然,这与任何其他Java项目相同,所以它应该没有惊喜。

我们可以创建由“主应用程序”和多个库模块组成的多模块项目。

但我们必须小心,因为每个JAR文件只能有一个模块。

当我们设置构建文件时,我们需要确保将项目中的每个模块捆绑为单独的jar。

3.默认模块

当我们安装Java 9时,我们可以看到JDK现在有一个新的结构。

他们拍摄了所有原始包,将它们移入新模块系统。

我们可以在命令行中输入这些模块:

java  - 列表模块

这些模块分为四个主要组:Java,JavaFX,JDK,甲骨文

java.模块是核心SE语言规范的实现类。

javafx模块是FX UI库。

JDK本身需要的任何东西都保存在jdk.模块。

最后,任何特定于oracle的东西都在甲骨文模块。

4.模块声明

要设置模块,我们需要在命名的软件包的根目录下放置一个特殊文件module-info.java.

此文件称为模块描述符,并包含构建和使用我们新模块所需的所有数据。

我们使用声明构建模块,其正文要么空为空或由模块指令组成:

模块myModuleName{//所有指令都是可选的}

以。开始模块声明模块关键字,我们按照模块的名称遵循。

该模块将使用此声明,但我们通常需要更多信息。

这就是模块指令进来的地方。

4.1。需要

我们的第一个指令是需要。此模块指令允许我们声明模块依赖项:

模块my.module {需要module.name;}

现在,my.module具有运行时和编译时间依赖module.name.name.

当我们使用此指令时,我们的模块可以通过从依赖项导出的所有公共类型。

4.2。需要静态

有时我们编写引用另一个模块的代码,但我们库的用户永远不会使用。

例如,当出现另一个日志模块时,我们可以编写一个实用程序函数来漂亮地打印我们的内部状态。但是,并不是我们库的每个使用者都想要这个功能,而且他们不希望包含额外的日志记录库。

在这些情况下,我们希望使用可选的依赖性。通过使用需要静态指令,我们创建仅编译时依赖性:

模块my.module {需要静态模块.Name;}

4.3。需要传递

我们通常与图书馆合作,使我们的生活更容易。

但是,我们需要确保在我们的代码中带来的任何模块也将引入这些额外的“传递”依赖项,或者他们无法正常工作。

幸运的是,我们可以使用需要传递指令强制任何下游消费者也读取我们所需的依赖项:

模块my.module {需要传递博物馆.Name;}

现在,当一个开发人员需要my.module,他们也不会说需要module.name.为我们的模块仍然工作。

4.4。出口

默认情况下,模块不会将任何API暴露给其他模块。强封装是首先创建模块系统的关键激励因素之一。

我们的代码明显更安全,但现在我们需要明确地将API明确地打开世界,如果我们希望它可以使用。

我们使用出口指令公开命名包的所有公共成员:

my.module {exports com.my.package.name;}

现在,有人做的时候需要my.module,它们将访问我们的com.my.package.name包裹,但不是其他任何包裹。

4.5。出口......

我们可以用出口......向世界开放公众课程。

但是,如果我们不希望整个世界访问我们的API怎么办?

我们可以限制哪些模块可以使用的是我们的API出口......指令。

类似于出口指令,我们声明一个包导出。但是,我们还列出了哪些模块,我们允许将此包导入此包需要。让我们看看这看起来像:

模块my.module {导出com.my.package.name to com.pecific.package;}

4.6。用途

一个服务是可以是特定接口或抽象类的实现消耗由其他课程。

我们指定我们的模块消耗的服务用途指令。

注意类名是服务的界面或抽象类,而不是实现类:

模块my.module {使用class.name;}

我们应该注意到这里有一个差异需要指令和用途指令。

我们可能需要提供我们想要消耗的服务的模块,但该服务从其传递依赖项之一实现接口。

而不是强迫我们的模块需要全部attrisive依赖项,以防万一,我们使用用途指令将所需界面添加到模块路径。

4.7。为…提供了

模块也可以是一个服务提供者其他模块可以消耗。

指令的第一部分是提供了关键字。这里是我们放置接口或抽象类名的地方。

接下来,我们有指令,其中我们提供了实现类名实施界面或延伸抽象课。

这是它看起来像合的东西:

模块my.module {提供myInterfaceImpl提供MyInterface;}

4.8。打开

我们之前提到过,封装是该模块系统设计的驾驶动机。

在Java 9之前,可以使用反射来检查包中的每一种类型和成员,甚至是私人那些。没有什么是真正封装的,这可以为图书馆的开发人员开辟各种问题。

因为Java 9强制执行强封装,我们现在必须明确授予其他模块的权限来反思我们的课程。

如果我们想继续允许充分反射作为旧版本的Java所做的,我们可以简单地打开整个模块起来:

我打开模块。模块{}

4.9。打开

如果我们需要允许反射私人类型,但我们不希望所有的代码都暴露,我们可以使用打开指令揭露特定包裹。

但请记住,这将打开整个世界的包装,所以确保你想要的是:

我的模块。模块{ opens com.my.package; }

4.10。打开......

好的,所以有时反射很棒,但我们仍然想要尽可能多的安全性封装我们可以选择性地将我们的包打开到预先批准的模块列表中,在这种情况下使用打开......指示:

我的模块。模块{ opens com.my.package to moduleOne, moduleTwo, etc.; }

5.命令行选项

到目前为止,Maven和Gradle已经添加了对Java 9模块的支持,所以您不需要做很多手工构建项目的工作。然而,知道这些仍然很有价值如何使用来自命令行的模块系统。

我们将在下面的完整示例中使用命令行,以帮助巩固我们心目中整个系统的工作方式。

  • 模块路径- - - - - -我们使用模块路径选项指定模块路径。这是包含模块的一个或多个目录的列表。
  • 添加读取- 而不是依赖模块声明文件,我们可以使用相同的命令行需要指令;-add-reads
  • 添加导出- - - - - -命令行更换出口指令。
  • 加入- - - - - -取代打开子句。
  • 添加模块- - - - - -将模块列表添加到默认模块集中
  • 列表模块- - - - - -输出所有模块及其版本字符串的列表
  • 补丁模块-在模块中添加或覆盖类
  • 非法访问=许可证|警告|拒绝- 通过显示单个全局警告,可以放松强大的封装,显示每次警告,或者因错误而失败。默认为许可证

6.可见性

我们应该花一点时间谈论我们的代码的可见性。金宝搏官网188be

许多库都依赖于反射来发挥它们的魔力(JUnit和Spring想到)。

默认在Java 9中,我们将只要可以访问导出包中的公共类、方法和字段。即使我们使用反射来访问和调用非公共成员可移(true),我们将无法访问这些成员。

我们可以用打开,打开, 和打开......授予仅运行运行的反射访问的选项。笔记,这是运行时!

我们将无法编制私人类型,无论如何我们都不需要。

如果我们必须访问模块以进行反射,而且我们不是该模块的所有者(即,我们不能使用打开......指令),那么就可以使用命令行-Add-oppens.选项以允许自己的模块反射访问运行时对锁定的模块。

这里唯一的警告是您需要访问用于运行该模块的命令行参数以便工作。

7.把它整合在一起

现在我们知道模块是什么以及如何使用它们让我们继续前进并建立一个简单的项目来演示我们刚才学识的所有概念。

为了保持简单的事情,我们不会使用Maven或Gradle。相反,我们将依靠命令行工具来构建我们的模块。

7.1。设置我们的项目

首先,我们需要设置我们的项目结构。我们将创建多个目录以组织我们的文件。

首先创建项目文件夹:

MKDIR模块 - 项目CD模块 - 项目

这是我们整个项目的基础,所以在这里添加一些文件,比如Maven或Gradle构建文件、其他源目录和资源。

我们还将一个目录放在持有所有项目特定模块。

接下来,我们创建一个模块目录:

mkdir简单模块

这是我们的项目结构看起来像:

模块金宝搏188体育.app |  -  com 金宝搏188体育|  -  baeldung |  - 模块|  - 主要

7.2。我们的第一个模块

现在我们有基本结构到位,让我们添加我们的第一个模块。

在下面简单模块目录,创建一个调用的新目录你好..Modules.

我们可以将任何我们想要的任何东西命名,但遵循包命名规则(即分开单词等的期限)。如果我们想要,我们甚至可以使用主包的名称作为模块名称,但通常,我们想坚持使用同名我们将使用该模块的jar。

在我们的新模块下,我们可以创建我们想要的包。在我们的情况下,我们将创建一个包结构:

com.金宝搏188体育baeldung.modules.hello

接下来,创建一个名为的新类hellomodules.java.在这个包中。我们将保留简单的代码:

包com.baeldun金宝搏188体育g.modules.hello;public class HelloModules {public static void doSomething() {System.out;println(“你好,模块!”);}}

最后,在里面你好..Modules.根目录,在我们的模块描述符中添加;module-info.java.:

模块你好。模块s { exports com.baeldung.modules.hello; }

要保持这个例子简单,我们所做的就是导出所有公共成员com.金宝搏188体育baeldung.modules.hello包中。

7.3。我们的第二个模块

我们的第一个模块很棒,但它什么都不做。

我们可以创建现在使用它的第二个模块。

在我们的简单模块目录,创建一个调用的另一个模块目录main.app.。我们将在此时间开始使用模块描述符:

modum.app {需要hello.modules;}

我们不需要向外界暴露任何东西。相反,我们需要做的就是依赖于我们的第一个模块,因此我们可以访问它出口的公共课程。

现在我们可以创建一个使用它的应用程序。

创建一个新的包结构:com.金宝搏188体育baeldung.modules.main.

现在,创建一个名为的新类文件MainApp.java。

包com.baeldun金宝搏188体育g.modules.main;导入com.baeld金宝搏188体育ung.modules.hello.hellomodules;公共类MainApp {公共静态void main(String [] args){hellomodules.dosomething();}}

这就是我们需要演示模块所需的所有代码。我们的下一步是从命令行构建和运行此代码。

7.4。建立我们的模块

要构建我们的项目,我们可以创建一个简单的Bash脚本并将其放在我们项目的根目录中。

创建一个名为compile-simple-modules.sh:

#!/ usr / bin / env bash javac -d oundir --module-source-path simple-modules $(查找简单模块-name“* .java”)

这个命令有两部分,javac命令。

命令只是输出所有列表。java.在我们的简单模块目录下的文件。然后,我们可以将该列表直接输入到Java编译器中。

我们唯一要做的方式与旧版本的Java有关是提供一个模块 - 源路径参数通知编译器它是构建模块。

我们运行此命令后,我们将有一个轶事文件夹中有两个编译模块。

7.5。运行我们的代码

现在我们最终可以运行我们的代码来验证模块正常工作。

在项目的根目录中创建另一个文件:run-simple-module-app.sh.

#!/ usr / bin / env bash java --module-path outirtir-m main.ap金宝搏188体育p/com.baeldung.modules.main.mainapp

要运行模块,我们必须至少提供模块路径和主班。如果所有工作,您应该看到:

> $ ./run-simple-module-app.sh hello,模块!

7.6。添加服务

现在我们对如何构建模块的基本了解,让我们变得更加复杂。

我们将看到如何使用为…提供了用途指令。

首先定义一个新文件你好..Modules.模块命名HelloInterface.java.:

公共接口Hellointerface {void sayhello();}

为了简化操作,我们将使用现有的接口来实现这个接口hellomodules.java.班级:

公共类Hellomodules实现Hellointerface {public static void dosomething(){system.out.println(“hello,modules!”);public void sayhello(){system.out.println(“Hello!”);}}

这就是我们需要做的一切来创建一个服务

现在,我们需要告诉世界我们的模块提供此服务。

将以下内容添加到我们的module-info.java.:

提供com.baeldun金宝搏188体育g.modules.hello.hellointerface与com.baeldung.modules.hello.hellomodules;

正如我们所看到的,我们声明界面以及哪个类实现它。

接下来,我们需要消耗它服务。在我们的main.app.模块,让我们添加以下内容module-info.java.:

使用com.bae金宝搏188体育ldung.modules.hello.hellointerface;

最后,在我们的主方法中,我们可以通过a使用这个服务ServiceLoader.:

erable  services = serviceloader.load(hellointerface.class);hellointerface service = services.iterator()。下一个();service.sayhello();

编译和运行:

#> ./run-simple-module-app.sh hello,模块!你好!

我们使用这些指令更明确地了解我们的代码如何使用。金宝搏官网188be

我们可以将实现放入私有包,同时在公共包装中公开界面。

这使得我们的代码更安全,而且额外的开销很少。

继续,尝试一些其他指令,以了解更多有关模块以及它们的工作方式。金宝搏官网188be

8.将模块添加到未命名的模块

未命名的模块概念与默认包类似。因此,它不被视为真实的模块,但可以被视为默认模块。

如果类不是命名模块的成员,那么它将被自动被视为此未命名模块的一部分。

有时,为了确保模块图中特定的平台、库或服务提供程序模块,我们需要向默认根集添加模块。例如,当我们尝试用Java 9编译器按原样运行Java 8程序时,我们可能需要添加模块。

一般来说,将命名模块添加到默认的根模块集的选项是-add-modules (, <模块>)*在哪里<模块>是模块名。

例如,为所有人提供访问java.xml.bind.bind.模块语法是:

——添加模块java.xml.bind

要在Maven中使用它,我们可以嵌入相同的内容Maven-Compiler-Plugin:

<插件> < groupId > org.apache.maven。 maven-compiler-plugin 3.8.0  9 9 0 1——add-modules2 3 java.xml。绑定   

9.结论

在这一广泛的指南中,我们专注于并涵盖了新的Java 9模块系统的基础知识。

我们首先谈论模块是什么。金宝搏官网188be

接下来,我们讨论了如何发现JDK金宝搏官网188be中包含哪些模块。

我们还详细介绍了模块声明文件。

通过讨论构建模块所需的各种命令行参数,我们对理论进行了总结。金宝搏官网188be

最后,我们将所有以前的知识放入实践中,并创建了一个内置在模块系统顶部的简单应用程序。

要查看此代码等等,请务必在github上看看它

Java底部

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>查看课程
2注释
最老的
最新
内联反馈
查看所有评论
本文评论关闭!