A Guide to Java 9 Modularity

原文地址:https://www.baeldung.com/java-9-modularity

以下中文翻译为作者根据自己的理解+Google+YouDao作为参考翻译而得,存在偏颇之处请读者查看对应原文,自行理解。

1. Overview

Java 9 introduces a new level of abstraction above packages, formally known as the Java Platform Module System (JPMS), or “Modules” for short.

Java 9基于package引入了一个新的抽象级别,正式称为Java平台模块系统(JPMS)或“模块”。

In this tutorial, we'll go through the new system and discuss its various aspects.

在本教程中,我们将通过一个新系统来探索其各个方面。

We'll also build a simple project to demonstrate all concepts we'll be learning in this guide.

我们也将构建一个简单的工程来演示我们在本教程中的学到的所有的概念。

2. What's a Module?

First of all, we need to understand what a module is before we can understand how to use them.

首先,我们需要先理解模块是什么,然后才能理解如何使用它们。

A Module is a group of closely related packages and resources along with a new module descriptor file.

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

In other words, it's a “package of Java Packages” abstraction that allows us to make our code even more reusable.

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

2.1. Packages

The packages inside a module are identical to the Java packages we've been using since the inception of Java.

模块中的包与我们自Java诞生以来一直使用的Java包完全相同。

When we create a module, we organize the code internally in packages just like we previously did with any other project.

当我们创建一个模块时,我们在包中内部组织代码,就像我们以前在任何其他项目中所做的那样

Aside from organizing our code, packages are used to determine what code is publicly accessible outside of the module. We'll spend more time talking about this later in the article.

除了组织我们的代码,包还用于确定哪些代码可以在模块外部公开被访问。我们将在本文后面花更多时间讨论这个问题。

2.2. Resources

Each module is responsible for its resources, like media or configuration files.

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

Previously we'd put all resources into the root level of our project and manually manage which resources belonged to different parts of the application.

以前,我们将所有资源放在项目的根目录下,并手动管理这些资源到应用程序的不同位置。

With modules, we can ship required images and XML files with the module that needs it, making our projects much easier to manage.

通过模块,我们可以将所需的图像和XML文件与需要它的模块一起发布,从而使我们的项目更容易管理。

2.3. Module Descriptor

When we create a module, we include a descriptor file that defines several aspects of our new module:

当我们创建一个模块时,我们需要包含一个描述符文件,用于定义新模块的这几个方面:

  • Name – the name of our module 模块的名称
  • Dependencies – a list of other modules that this module depends on 该模块依赖的其他模块列表
  • Public Packages – a list of all packages we want accessible from outside the module 我们希望从模块外部访问的所有包的列表
  • Services Offered – we can provide service implementations that can be consumed by other modules 我们可以提供服务的具体实现用于其他模块的消费使用
  • Services Consumed – allows the current module to be a consumer of a service 允许当前模块成为服务的消费者
  • Reflection Permissions – explicitly allows other classes to use reflection to access the private members of a package 显式地允许其他类使用反射来访问包的私有成员

The module naming rules are similar to how we name packages (dots are allowed, dashes are not). It's very common to do either project-style (my.module) or Reverse-DNS (com.baeldung.mymodule) style names. We'll use project-style in this guide.

模块命名规则类似于我们命名包的方式(允许使用点,不允许使用破折号)。使用 project-style (my.module)或Reverse-DNS (com.baeldung.mymodule)风格的名称是很常见的。在本指南中,我们将使用project-style

We need to list all packages we want to be public because by default all packages are module private.

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

The same is true for reflection. By default, we cannot use reflection on classes we import from another module.

反射也是如此。默认情况下,不能对从其他模块导入的类使用反射。

Later in the article, we'll look at examples of how to use the module descriptor file.

在本文的末尾,我们将学习如何使用模块描述符文件的示例。

2.4. Module Types

There are four types of modules in the new module system:

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

  • System Modules – These are the modules listed when we run the list-modules command above. They include the Java SE and JDK modules.
  • 系统模块 – 这些是运行上面的list-modules命令时列出的模块。它们包括Java SE和JDK模块。
  • Application Modules – These modules are what we usually want to build when we decide to use Modules. They are named and defined in the compiled module-info.class file included in the assembled JAR.
  • 应用模块 – 当我们决定使用模块时,这些模块是我们通常想要构建的。它们是在组装的JAR中包含的编译过的module-info.class文件中命名和定义的。
  • Automatic Modules – We can include unofficial modules by adding existing JAR files to the module path. The name of the module will be derived from the name of the JAR. Automatic modules will have full read access to every other module loaded by the path.
  • 自动模块 – 我们可以通过将现有JAR文件添加到模块路径中来包含非官方模块。模块的名称将从JAR的名称派生出来。自动模块将对路径加载的所有其他模块具有完全读访问权。
  • Unnamed Module – When a class or JAR is loaded onto the classpath, but not the module path, it's automatically added to the unnamed module. It's a catch-all module to maintain backward compatibility with previously-written Java code.
  • 未命名模块 – 当类或JAR加载到类路径而不是模块路径时,它会自动添加到未命名的模块。它是一个全能模块,用于维护以前编写的Java代码,以保证向后的兼容性。

2.5. Distribution

Modules can be distributed one of two ways: as a JAR file or as an “exploded” compiled project. This, of course, is the same as any other Java project so it should come as no surprise.

模块可以通过以下两种方式分发:作为JAR文件分发,或者作为已编译项目分发。当然,这与任何其他Java项目是一样的,所以应该不会感到惊讶。

We can create multi-module projects comprised of a “main application” and several library modules.

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

We have to be careful though because we can only have one module per JAR file.

我们必须要注意的是每个jar文件只能有一个模块。

When we set up our build file, we need to make sure to bundle each module in our project as a separate jar.

当我们构建项目时,需要确保将项目中的每个模块打包为一个单独的jar。

3. Default Modules

When we install Java 9, we can see that the JDK now has a new structure.

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

They have taken all the original packages and moved them into the new module system.

他们已经将所有的原始包转移到新的模块系统中。

We can see what these modules are by typing into the command line:

我们可以通过输入以下命令来查看这些模块:

java --list-modules

These modules are split into four major groups: java, javafx, jdk, and Oracle.

这些模块包含四个主要的分组:java,javafx,jdk,Oracle

java modules are the implementation classes for the core SE Language Specification.

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

javafx modules are the FX UI libraries.

javafx模块是FX UI的依赖模块。

Anything needed by the JDK itself is kept in the *jdk* modules.

JDK自身所需要的都放在jdk模块中。

And finally, anything that is Oracle-specific is in the *oracle* modules.

最后,任何与Oracle相关的内容放在oracle模块中。

4. Module Declarations

To set up a module, we need to put a special file at the root of our packages named module-info.java.

构建一个模块时,需要添加一个module-info.java文件,放在包的根路径下。

This file is known as the module descriptor and contains all of the data needed to build and use our new module.

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

We construct the module with a declaration whose body is either empty or made up of module directives:

该文件的主体为空,或者包含读个多个模块指令。

module myModuleName {
// all directives are optional
}

We start the module declaration with the module keyword, and we follow that with the name of the module.

通过module关键字作为开头,后面加上模块的名称来作为模块定义的开头。

The module will work with this declaration, but we'll commonly need more information.

一个模块需要如此声明才能正常使用,但通常还会包含其他东西。

That is where the module directives come in.

具体描述模块的指令就加入其中。

4.1. Requires

Our first directive is requires. This module directive allows us to declare module dependencies:

首先介绍requires。该模块指令允许我们声明模块的依赖项:

module my.module {
requires module.name;
}

Now, my.module has both a runtime and a compile-time dependency on module.name.

此时,my.module模块包含运行时和编译时所需的module.name模块。

And all public types exported from a dependency are accessible by our module when we use this directive.

当我们使用此指令时,我们的模块可以从依赖中访问到所有的公开的内容。

4.2. Requires Static

Sometimes we write code that references another module, but that users of our library will never want to use.

有时我们需要引用另一个模块来写代码,但是实际运行项目时并不需要该模块的参与。(例如:lombok)

For instance, we might write a utility function that pretty-prints our internal state when another logging module is present. But, not every consumer of our library will want this functionality, and they don't want to include an extra logging library.

例如,我们可能会编写一个utility函数时需要另一个logging模块来帮助格式化打印内部状态。但是并非每个使用我们模块的都需要这个函数,并且他们并不想引入一个额外的日志依赖,或者他们本身就自带了日志系统。

In these cases, we want to use an optional dependency. By using the requires static directive, we create a compile-time-only dependency:

在这些情况下,我们希望使用可选的依赖项。通过使用requires static指令,我们引入了一个仅编译时参与的依赖项:

module my.module {
requires static module.name;
}

4.3. Requires Transitive

We commonly work with libraries to make our lives easier.

But, we need to make sure that any module that brings in our code will also bring in these extra ‘transitive' dependencies or they won't work.

但是,我们需要确保在我们的代码中添加的任何模块也将引入这些额外的“transitive”依赖项,否则他们无法正常工作。

Luckily, we can use the requires transitive directive to force any downstream consumers also to read our required dependencies:

幸运的是,我们可以使用requires transitive指令来强制任何下游消费者也来阅读我们所需的依赖项。

module my.module {
requires transitive module.name;
}

Now, when a developer requires my.module, they won't also have also to say requires module.name for our module to still work.

现在,当开发者引入my.module时,他们不会抱怨说还需要引入module.name模块才能正常使用my.module模块。

4.4. Exports

By default, a module doesn't expose any of its API to other modules. This strong encapsulation was one of the key motivators for creating the module system in the first place.

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

Our code is significantly more secure, but now we need to explicitly open our API up to the world if we want it to be usable.

如此我们的代码将更加安全,但是如果我们希望它能够被使用的话,就需要将其暴露到外界。

We use the exports directive to expose all public members of the named package:

我们使用exports指令来暴露已声明package中的所有public成员

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

Now, when someone does requires my.module, they will have access to the public types in our com.my.package.name package, but not any other package.

现在,当有人requires my.module时,他们将可以访问到我们com.my.package.name包下的public成员,但是其他包无法访问。

4.5. Exports … To

We can use exports…to to open up our public classes to the world.

我们可以使用exports...to开放给外界访问public类。

But, what if we don't want the entire world to access our API?

但是如果我们不希望暴露API给到所有使用者该怎么办呢?

We can restrict which modules have access to our APIs using the exports…to directive.

我们可以使用exports...to指令,来限制哪些模块可以访问我们的API。

Similar to the exports directive, we declare a package as exported. But, we also list which modules we are allowing to import this package as a requires. Let's see what this looks like:

类似于export指令,我们可以声明需要暴露的包, 但是还指定哪些模块可以访问这个包。 看起来就像这样:

module my.module {
// 将com.my.package.name仅暴露给com.specific.package访问
export com.my.package.name to com.specific.package;
}

4.6. Uses

A service is an implementation of a specific interface or abstract class that can be consumed by other classes.

服务是一个可以被其他类消费的具体接口实现类或抽象类的扩展。

We designate the services our module consumes with the uses directive.

可以通过uses指令来指定我们模块可以消费的服务。

Note that the class name we use is either the interface or abstract class of the service, not the implementation class:

请注意,uses 后面的类名是服务的接口或抽象类,而不是实现类

module my.module {
uses class.name;
}

We should note here that there's a difference between a requires directive and the uses directive.

我们应该注意到requiresuses指令之间的差异。

We might require a module that provides a service we want to consume, but that service implements an interface from one of its transitive dependencies.

我们可能需要一个可供消费所需服务的模块,但是该服务实现了一个来自它的一个传递依赖项的接口。

Instead of forcing our module to require all transitive dependencies just in case, we use the uses directive to add the required interface to the module path.

我们可以使用uses指令将所需的接口添加到模块路径,而不是为了以防万一强迫我们的模块导入所有的传递依赖。

4.7. Provides … With

A module can also be a service provider that other modules can consume.

一个模块也可以是可供其他模块使用的服务提供者。

The first part of the directive is the provides keyword. Here is where we put the interface or abstract class name.

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

Next, we have the with directive where we provide the implementation class name that either implements the interface or extends the abstract class.

接下来,我们有with指令,其中我们需要提供实现类名,该实现类名要么实现了接口,要么扩展了抽象类。

Here's what it looks like put together:

下面是它组合的样子:

module my.module {
provides MyInterface with MyInterfaceImpl;
}

4.8. Open

We mentioned earlier that encapsulation was a driving motivator for the design of this module system.

我们前面提到过封装是设计这个模块系统的一个驱动因素。

Before Java 9, it was possible to use reflection to examine every type and member in a package, even the private ones. Nothing was truly encapsulated, which can open up all kinds of problems for developers of the libraries.

Java 9之前,可以使用反射来检查包中所有类和成员,甚至private的。这并不是真正的封装,还可能会给库的开发人员带来各种问题。

Because Java 9 enforces strong encapsulation, we now have to explicitly grant permission for other modules to reflect on our classes.

因为Java 9强制执行强封装我们现在必须显式地授予其他模块反射类的权限。

If we want to continue to allow full reflection as older versions of Java did, we can simply open the entire module up:

如果我们想继续像以前的Java版本那样允许完全反射,我们可以简单地open整个模块:

open module my.module {
}

4.9. Opens

If we need to allow reflection of private types, but we don't want all of our code exposed, we can use the *opens* directive to expose specific packages.

But remember, this will open the package up to the entire world, so make sure that is what you want:

module my.module {
opens com.my.package;
}

4.10. Opens … To

Okay, so reflection is great sometimes, but we still want as much security as we can get from encapsulation. We can selectively open our packages to a pre-approved list of modules, in this case, using the *opens…to* directive:

反射总体是好的,但我们仍然希望从封装中获得尽可能多的安全性。我们可以有选择地打开我们的包到一个预先批准的模块列表,在本例中,使用* opened…to*指令:

module my.module {
opens com.my.package to moduleOne, moduleTwo, etc.;
}

5. Command Line Options

By now, support for Java 9 modules has been added to Maven and Gradle, so you won't need to do a lot of manual building of your projects. However, it's still valuable to know how to use the module system from the command line.

到目前为止,Maven和Gradle已经添加了对Java 9模块的支持,因此您不需要进行大量的项目手工构建。然而,知道如何从命令行使用模块系统仍然是有必要的。

We'll be using the command line for our full example down below to help solidify how the entire system works in our minds.

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

  • module-path We use the –module-path option to specify the module path. This is a list of one or more directories that contain your modules. 我们使用 --module-path 选项来指定模块路径。这是包含模块的一个或多个目录的列表。(Windows用 ; 分号隔开,Mac、Linux用 : 冒号)
  • add-reads – Instead of relying on the module declaration file, we can use the command line equivalent of the requires directive; –add-reads. 代替依赖于模块声明文件,我们可以使用命令行中等效的requires; --add-reads 指令。
  • add-exports Command line replacement for the exports directive. 命令行替换exports指令。
  • add-opens Replace the open clause in the module declaration file. 替换模块声明文件中的open子句。
  • add-modules Adds the list of modules into the default set of modules. 将模块列表添加到默认模块集中
  • list-modules Prints a list of all modules and their version strings. 打印所有模块及其版本字符串的列表。
  • patch-module – Add or override classes in a modules. 在模块中添加或覆盖类。
  • illegal-access=permit|warn|deny – Either relax strong encapsulation by showing a single global warning, shows every warning, or fails with errors. The default is permit. 通过显示单个全局警告,可以放松强封装,显示每次警告,或者因错误而失败。 默认为允许

6. Visibility

We should spend a little time talking about the visibility of our code.

我们应该花一点时间讨论下代码的可见性。

A lot of libraries depend on reflection to work their magic (JUnit and Spring come to mind).

许多库依赖于反射操作来实现他们的神奇功能(JUnit和Spring就是如此)。

By default in Java 9, we will only have access to public classes, methods, and fields in our exported packages. Even if we use reflection to get access to non-public members and call setAccessible(true), we won't be able to access these members.

在Java 9中,默认情况下我们将访问我们暴露的包中的公共类,方法和字段。 即使我们使用反射来访问非公共成员并将其设置为可访问的,我们也无法访问这些成员。

We can use the open, opens, and opens…to options to grant runtime-only access for reflection. Note, this is runtime-only!

我们可以使用openopensopen...to选项授予反射以运行时的访问权限。 注意,这是运行时!

We won't be able to compile against private types, and we should never need to anyway.

我们将无法针对私有类型进行编译,无论如何都不应该这样做。

If we must have access to a module for reflection, and we're not the owner of that module (i.e., we can't use the opens…to directive), then it's possible to use the command line –add-opens option to allow own modules reflection access to the locked down module at runtime.

如果我们必须通过反射来访问一个模块,并且我们不是那个模块的所有者(即,我们不能使用 opened…to 指令),那么可以使用命令行 --add-opened 选项来允许自己的模块在运行时反射访问锁定的模块。

The only caveat here's that you need to have access to the command line arguments that are used to run a module for this to work.

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

7. Putting It All Together

Now that we know what a module is and how to use them let's go ahead and build a simple project to demonstrate all the concepts we just learned.

既然我们知道模块是什么以及如何使用它们,接下来建立一个简单的项目来演示我们刚才学习的所有概念吧。

To keep things simple, we won't be using Maven or Gradle. Instead, we'll rely on the command line tools to build our modules.

简单起见,我们不会使用Maven或Gradle。 相反,我们将依靠命令行工具来构建我们的模块。

7.1. Setting Up Our Project

First, we need to set up our project structure. We'll create several directories to organize our files.

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

Start by creating the project folder:

首先创建项目文件夹:

mkdir module-project
cd module-project

This is the base of our whole project, so add files in here such as Maven or Gradle build files, other source directories, and resources.

这是我们整个项目的基础,因此在此添加文件,如Maven或Gradle构建文件,其他源目录和资源。

We also put a directory to hold all our project specific modules.

我们还将创建一个目录用来包含所有模块。

Next, we create a module directory:

mkdir simple-modules

Here's what our project structure will look like:

项目结构如下:

module-project
|- // src if we use the default package
|- // build files also go at this level
|- simple-modules
|- hello.modules
|- com
|- baeldung
|- modules
|- hello
|- main.app
|- com
|- baeldung
|- modules
|- main

7.2. Our First Module

Now that we have the basic structure in place, let's add our first module.

现在我们已经有了一个基础结构,开始添加第一个模块吧。

Under the simple-modules directory, create a new directory called hello.modules.

simple-modules目录下,创建一个叫做hello.modules的文件夹。

We can name this anything we want but follow package naming rules (i.e., periods to separate words, etc.). We can even use the name of our main package as the module name if we want, but usually, we want to stick to the same name we would use to create a JAR of this module.

我们可以随意命名,但请遵循包命名规则(如,分隔单词的点 . )。 如果我们想要,我们甚至可以使用主要包的名称作为模块名称,但通常,我们希望使用与jar文件同名的方式来命名模块。

Under our new module, we can create the packages we want. In our case, we are going to create one package structure:

在新模块中,可以创建我们想要的包。在我们的例子中,包结构如下:

com.baeldung.modules.hello

Next, create a new class called HelloModules.java in this package. We will keep the code simple:

接下来,在包中新建一个名为HelloModules.java的类,尽可能让代码简单一点:

package com.baeldung.modules.hello;

public class HelloModules {
public static void doSomething() {
System.out.println("Hello, Modules!");
}
}

And finally, in the hello.modules root directory, add in our module descriptor; module-info.java:

最后,在hello.modules的的根路径下,添加module-info.java文件:

module hello.modules {
exports com.baeldung.modules.hello;
}

To keep this example simple, all we are doing is exporting all public members of the com.baeldung.modules.hello package.

为了保持示例简单,我们导出了 com.baeldung.modules.hello 包中的所有公共成员。

7.3. Our Second Module

Our first module is great, but it doesn't do anything.

第一个模块貌似啥都不能干。

We can create a second module that uses it now.

现在来创建第二个模块,并使用第一个模块。

Under our simple-modules directory, create another module directory called main.app. We are going to start with the module descriptor this time:

在我们的简单模块目录下,创建一个名为 main.app 的另一个模块目录。 我们将同时使用模块描述符:

module main.app {
requires hello.modules;
}

We don't need to expose anything to the outside world. Instead, all we need to do is depend on our first module, so we have access to the public classes it exports.

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

Now we can create an application that uses it.

现在创建一个应用来使用它。

Create a new package structure: com.baeldung.modules.main.

新建一个包:com.baeldung.modules.main

Now, create a new class file called MainApp.java.

然后新建一个MainApp.java类。

package com.baeldung.modules.main;

import com.baeldung.modules.hello.HelloModules;

public class MainApp {
public static void main(String[] args) {
HelloModules.doSomething();
}
}

And that is all the code we need to demonstrate modules. Our next step is to build and run this code from the command line.

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

7.4. Building Our Modules

To build our project, we can create a simple bash script and place it at the root of our project.

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

Create a file called compile-simple-modules.sh:

新建一个compile-simple-modules.sh

#!/usr/bin/env bash
javac -d outDir --module-source-path simple-modules $(find simple-modules -name "*.java")

There are two parts to this command, the javac and find commands.

此命令有两个部分,javacfind 命令。

The find command is simply outputting a list of all .java files under our simple-modules directory. We can then feed that list directly into the Java compiler.

find命令仅简单的输出simple-modules目录下的所有*.java*文件,然后将这些文件直接输出给Java编译器。

The only thing we have to do differently than the older versions of Java is to provide a module-source-path parameter to inform the compiler that it's building modules.

我们唯一要做的不同之处在于旧版本的Java是提供一个 modul-source-path 参数来通知编译器是构建模块。

Once we run this command, we will have an outDir folder with two compiled modules inside.

运行此命令后,我们将拥有一个 outDir 文件夹,其中包含两个编译模块。

7.5. Running Our Code

And now we can finally run our code to verify modules are working correctly.

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

Create another file in the root of the project: run-simple-module-app.sh.

新建另一个文件放在项目的根路径下:run-simple-module-app.sh.

#!/usr/bin/env bash
java --module-path outDir -m main.app/com.baeldung.modules.main.MainApp

To run a module, we must provide at least the module-path and the main class. If all works, you should see:

要运行模块,我们必须至少提供 module-path 和主类。 如果都正常执行了,你应该看到:

>$ ./run-simple-module-app.sh 
Hello, Modules!

7.6. Adding a Service

Now that we have a basic understanding of how to build a module, let's make it a little more complicated.

现在我们对如何构建模块有了基本了解,开始加点难度。

We're going to see how to use the provides…with and uses directives.

我们来了解下如何使用 provides…withuses 指令。

Start by defining a new file in the hello.modules module named HelloInterface.java:

首先在 hello.modules 模块中新建一个 HelloInterface.java

public interface HelloInterface {
void sayHello();
}

To make things easy, we're going to implement this interface with our existing HelloModules.java class:

HelloModules.java 类上实现此接口:

public class HelloModules implements HelloInterface {
public static void doSomething() {
System.out.println("Hello, Modules!");
}

public void sayHello() {
System.out.println("Hello!");
}
}

That is all we need to do to create a service.

这就是我们构建好的服务。

Now, we need to tell the world that our module provides this service.

现在我们需要对外公开我们的模块可以提供这个服务。

Add the following to our module-info.java:

添加如下内容到 module-info.java中:

provides com.baeldung.modules.hello.HelloInterface with com.baeldung.modules.hello.HelloModules;

As we can see, we declare the interface and which class implements it.

正如我们所看到的,我们声明了该接口以及哪个类实现了它。

Next, we need to consume this service. In our main.app module, let's add the following to our module-info.java:

接下来,我们消费一下service。在我们的 main.app 模块中,让我们将以下内容添加到我们的 module-info.java

uses com.baeldung.modules.hello.HelloInterface;

Finally, in our main method we can use this service via a ServiceLoader:

最后,在我们的主要方法中,我们可以通过ServiceLoader使用此服务:

Iterable<HelloInterface> services = ServiceLoader.load(HelloInterface.class);
HelloInterface service = services.iterator().next();
service.sayHello();

Compile and run:

#> ./run-simple-module-app.sh 
Hello, Modules!
Hello!

We use these directives to be much more explicit about how our code is to be used.

我们使用这些指令更明确地了解如何处理我们的代码。

We could put the implementation into a private package while exposing the interface in a public package.

我们可以将实现放入私有软件包,同时在公共包装中公开接口。

This makes our code much more secure with very little extra overhead.

这将使我们的代码更加安全,并且减小额外的开销。

Go ahead and try out some of the other directives to learn more about modules and how they work.

让我们继续尝试一些其他指令,以了解更多有关模块以及它们的工作方式。

8. Adding Modules to the Unnamed Module

The unnamed module concept is similar to the default package. Therefore, it's not considered a real module, but can be viewed as the default module.

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

If a class is not a member of a named module, then it will be automatically considered as part of this unnamed module.

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

Sometimes, to ensure specific platform, library, or service-provider modules in the module graph, we need to add modules to the default root set. For example, when we try to run Java 8 programs as-is with Java 9 compiler we may need to add modules.

有时,为了确保模块图中的特殊平台,库或服务提供者模块,我们需要将模块添加到默认根路径中。 例如,当我们尝试使用Java 9编译器运行Java 8程序时,我们可能需要添加模块。

In general, the option to add the named modules to the default set of root modules is –add-modules (,) where is a module name.

通常,**将命名模块添加到默认的根模块集的选项为 *-add-modules (, )***,其中 是模块名称。

For example, to provide access to all java.xml.bind modules the syntax would be:

例如,要提供对所有 java.xml.bind 模块的访问语法将是:

--add-modules java.xml.bind

To use this in Maven, we can embed the same to the maven-compiler-plugin:

要在Maven中使用此功能,我们可以将其嵌入maven-compiler-plugin

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>9</source>
<target>9</target>
<compilerArgs>
<arg>--add-modules</arg>
<arg>java.xml.bind</arg>
</compilerArgs>
</configuration>
</plugin>

9. Conclusion

In this extensive guide, we focused on and covered the basics of the new Java 9 Module system.

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

We started by talking about what a module is.

我们首先谈论模块是什么。

Next, we talked about how to discover which modules are included in the JDK.

接下来,我们讨论了如何发现JDK中包含哪些模块。

We also covered the module declaration file in detail.

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

We rounded out the theory by talking about the various command line arguments we'll need to build our modules.

我们通过谈论我们需要构建模块的各种命令行参数来彻底彻底理解。

Finally, we put all our previous knowledge into practice and created a simple application built on top of the module system.

最后,我们将所有之前的知识用于实践中,并创建了一个基于模块系统的简单应用程序。

To see this code and more, be sure to check it out over on Github.