A Guide to Java 9 Modularity – Java 9模块化指南

最后修改: 2018年 4月 18日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

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在包之上引入了一个新的抽象层次,正式称为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?

2.什么是模块?

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包的包 “的抽象,使我们能够使我们的代码更加可重复使用。

2.1. Packages

2.1.软件包

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

2.2.资源

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

2.3.模块描述符

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.

模块的命名规则类似于我们命名包的方式(允许使用点,不允许使用破折号)。项目风格(my.module)或反向DNS(com.baeldung.mymodule)风格的名字都很常见。在本指南中我们将使用项目风格。

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

2.4.模块类型

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.
  • 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.
  • 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.
  • 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.

2.5. Distribution

2.5.分布情况

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

3.默认模块

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

4.模块的声明

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

4.1.需要

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.modulemodule.name运行时和编译时的依赖性

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

当我们使用这个指令时,所有从依赖关系导出的公共类型都可以被我们的模块访问。

4.2. Requires Static

4.2.需要静态的

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

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

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.

例如,我们可能会写一个实用的函数,在另一个日志模块出现时,漂亮地打印出我们的内部状态。但是,并不是每一个我们库的消费者都想要这个功能,他们也不想包含一个额外的日志库。

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

4.3.要求传递性

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.

但是,我们需要确保任何引入我们代码的模块也会引入这些额外的 “横向 “依赖关系,否则它们就无法工作。

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.

现在,当开发者requires my.module时,他们不必同时说requires module.name,我们的模块就可以继续工作。

4.4. Exports

4.4.出口

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.

我们的代码明显更安全了,但现在我们需要明确地向世界开放我们的API,如果我们希望它是可用的。

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

我们使用exports指令来公开指定包的所有公共成员:

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包中的公共类型,但不能访问任何其他包。

4.5. Exports … To

4.5.出口……到

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

我们可以使用exports…to来向世界开放我们的公共类。

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:

exports指令类似,我们将一个包声明为已出口。但是,我们也列出了我们允许哪些模块导入这个包作为requires。让我们看看这看起来像什么。

module my.module {
    export com.my.package.name to com.specific.package;
}

4.6. Uses

4.6.用途

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:

注意,我们使用的类名是服务的接口或抽象类,而不是实现类

module my.module {
    uses class.name;
}

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

我们在这里应该注意到,requires指令和uses指令之间有区别。

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.

我们可能require一个模块,该模块提供了我们想要消费的服务,但是该服务实现了它的一个反式依赖的接口。

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

4.7.提供……与

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指令,在这里我们提供了实现类的名称,它要么是implements接口,要么是extends抽象类。

Here’s what it looks like put together:

这是它放在一起的样子。

module my.module {
    provides MyInterface with MyInterfaceImpl;
}

4.8. Open

4.8.打开

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 module my.module {
}

4.9. Opens

4.9.打开

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.

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

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

4.10.打开…它

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:

好吧,反射有时很好,但我们仍然希望从封装中获得尽可能多的安全性。我们可以有选择地对预先批准的模块列表开放我们的包,在这种情况下,使用opens…to指令

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

5. Command Line Options

5.命令行选项

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.
  • add-reads – Instead of relying on the module declaration file, we can use the command line equivalent of the requires directive; –add-reads.
  • add-exports – Command line replacement for the exports directive.
  • add-opens – Replace the open clause in the module declaration file.
  • 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

6.可见性

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 中,默认情况下,我们只能访问我们导出的包中的公共类、方法和字段。即使我们使用反射来获取对非公共成员的访问,并调用setAccessible(true),我们也无法访问这些成员。

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

我们可以使用openopensopens…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.

如果我们必须对一个模块进行反射访问,而我们又不是该模块的所有者(也就是说,我们不能使用opens…to指令),那么可以使用命令行-add-opens选项来允许自己的模块在运行时对锁定的模块进行反射访问。

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

7.把一切都放在一起

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

7.1.设置我们的项目

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

7.2.我们的第一个模块

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

7.3.我们的第二个模块

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:

在我们的simple-modules目录下,创建另一个名为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

7.4.构建我们的模块

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不同,就是提供一个module-source-path参数来通知编译器它正在构建模块。

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

一旦我们运行这个命令,我们将有一个outDir文件夹,里面有两个编译好的模块。

7.5. Running Our Code

7.5.运行我们的代码

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

7.6.添加一个服务

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

8.向无名模块添加模块

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 <module>(,<module>)* where <module> is a module name.

一般来说,将命名的模块添加到默认的根模块集中的选项是-add-modules <module>(,<module>)*其中<module>是一个模块名称。

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

9.结论

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.

要查看此代码和更多内容,请务必在Github上查看