1. Introduction
1.介绍
Following A Guide to Java 9 Modularity, in this article, we’re going to explore the java.lang.Module API that was introduced alongside the Java Platform Module System.
继Java 9模块化指南之后,在这篇文章中,我们将探讨与Java平台模块系统一起引入的java.lang.Module API。
This API provides a way to access a module programmatically, to retrieve specific information from a module, and generally to work with it and its ModuleDescriptor.
该API提供了一种以编程方式访问模块的方法,从模块中检索特定的信息,并通常与它和它的ModuleDescriptor一起工作。
2. Reading Module Information
2.阅读模块信息
The Module class represents both named and unnamed modules. Named modules have a name and are constructed by the Java Virtual Machine when it creates a module layer, using a graph of modules as a definition.
Module类同时代表了有名和无名的模块。有名字的模块有一个名字,由Java虚拟机在创建模块层时构建,使用一个模块图作为定义。
An unnamed module doesn’t have a name, and there is one for each ClassLoader. All types that aren’t in a named module are members of the unnamed module related to their class loader.
未命名的模块没有名字,每个类加载器都有一个。 所有不在命名模块中的类型都是与其类加载器有关的未命名模块的成员。
The interesting part of the Module class is that it exposes methods that allow us to retrieve information from the module, like the module name, the module classloader and the packages within the module.
Module类有趣的部分是它暴露了一些方法,允许我们从模块中检索信息,如模块名称、模块的classloader和模块中的包。
Let’s see how it’s possible to find out if a module is named or unnamed.
让我们看看如何能找出一个模块是有名字还是没名字的。
2.1. Named or Unnamed
2.1.有名或无名
Using the isNamed() method we can identify whether a module is named or not.
使用isNamed()方法,我们可以识别一个模块是否被命名。
Let’s see how we can see if a given class, like HashMap, is part of a named module and how we can retrieve its name:
让我们看看我们如何能看到一个给定的类,如HashMap,是否是一个命名模块的一部分,以及我们如何能检索它的名字。
Module javaBaseModule = HashMap.class.getModule();
assertThat(javaBaseModule.isNamed(), is(true));
assertThat(javaBaseModule.getName(), is("java.base"));
Let’s now define a Person class:
现在我们来定义一个Person类。
public class Person {
private String name;
// constructor, getters and setters
}
In the same way, as we did for the HashMap class, we can check if the Person class is part of a named module:
正如我们对HashMap类所做的那样,我们可以检查Person类是否是一个命名模块的一部分。
Module module = Person.class.getModule();
assertThat(module.isNamed(), is(false));
assertThat(module.getName(), is(nullValue()));
2.2. Packages
2.2.软件包
When working with a module, it might be important to know which packages are available within the module.
当使用一个模块时,知道模块中哪些包是可用的可能很重要。
Let’s see how we can check if a given package, for example, java.lang.annotation, is contained in a given module:
让我们看看如何检查一个给定的包,例如,java.lang.annotation,是否包含在一个给定的模块中。
assertTrue(javaBaseModule.getPackages().contains("java.lang.annotation"));
assertFalse(javaBaseModule.getPackages().contains("java.sql"));
2.3. Annotations
2.3.注释
In the same way, as for the packages, it’s possible to retrieve the annotations that are present in the module using the getAnnotations() method.
同样,对于包,可以使用getAnnotations()方法检索模块中存在的注释。
If there are no annotations present in a named module, the method will return an empty array.
如果一个命名的模块中没有注释,该方法将返回一个空数组。
Let’s see how many annotations are present in the java.base module:
让我们看看java.base模块中存在多少个注解。
assertThat(javaBaseModule.getAnnotations().length, is(0));
When invoked on an unnamed module, the getAnnotations() method will return an empty array.
当对一个未命名的模块调用时,getAnnotations()方法将返回一个空数组。
2.4. ClassLoader
2.4.类加载器
Thanks to the getClassLoader() method available within the Module class, we can retrieve the ClassLoader for a given module:
由于getClassLoader()方法在Module类中可用,我们可以为一个特定的模块检索ClassLoader。
assertThat(
module.getClassLoader().getClass().getName(),
is("jdk.internal.loader.ClassLoaders$AppClassLoader")
);
2.5. Layer
2.5.层
Another valuable information that could be extracted from a module is the ModuleLayer, which represents a layer of modules in the Java virtual machine.
另一个可以从模块中提取的有价值的信息是ModuleLayer,它代表了Java虚拟机中的一个模块层。
A module layer informs the JVM about the classes that may be loaded from the modules. In this way, the JVM knows exactly which module each class is a member of.
模块层通知JVM可能从模块中加载的类。通过这种方式,JVM确切地知道每个类是哪个模块的成员。
A ModuleLayer contains information related to its configuration, the parent layer and the set of modules available within the layer.
一个ModuleLayer包含与它的配置、父层和该层内可用的模块集有关的信息。
Let’s see how to retrieve the ModuleLayer of a given a module:
让我们看看如何检索一个给定模块的ModuleLayer。
ModuleLayer javaBaseModuleLayer = javaBaseModule.getLayer();
Once we have retrieved the ModuleLayer, we can access its information:
一旦我们检索到ModuleLayer,我们就可以访问其信息。
assertTrue(javaBaseModuleLayer.configuration().findModule("java.base").isPresent());
A special case is the boot layer, created when Java Virtual Machine is started. The boot layer is the only layer that contains the java.base module.
一个特殊的情况是启动层,在Java虚拟机启动时创建。启动层是唯一包含java.base模块的层。
3. Dealing With ModuleDescriptor
3.处理ModuleDescriptor的问题
A ModuleDescriptor describes a named module and defines methods to obtain each of its components.
一个ModuleDescriptor描述了一个命名的模块,并定义了获得其每个组件的方法。
ModuleDescriptor objects are immutable and safe for use by multiple concurrent threads.
ModuleDescriptor对象是不可变的,可供多个并发线程安全使用。
Let’s start by looking at how we can retrieve a ModuleDescriptor.
让我们先看看我们如何检索一个ModuleDescriptor.。
3.1. Retrieving a ModuleDescriptor
3.1.检索一个ModuleDescriptor
Since the ModuleDescriptor is tightly connected to a Module, it’s possible to retrieve it directly from a Module:
由于ModuleDescriptor与Module紧密相连,所以有可能直接从Module:检索它。
ModuleDescriptor moduleDescriptor = javaBaseModule.getDescriptor();
3.2. Creating a ModuleDescriptor
3.2.创建一个ModuleDescriptor
It’s also possible to create a module descriptor using the ModuleDescriptor.Builder class or by reading the binary form of a module declaration, the module-info.class.
也可以使用ModuleDescriptor.Builder类创建一个模块描述符,或者通过读取模块声明的二进制形式,即module-info.class。
Let’s see how we create a module descriptor using the ModuleDescriptor.Builder API:
让我们看看如何使用ModuleDescriptor.Builder API来创建一个模块描述符。
ModuleDescriptor.Builder moduleBuilder = ModuleDescriptor
.newModule("baeldung.base");
ModuleDescriptor moduleDescriptor = moduleBuilder.build();
assertThat(moduleDescriptor.name(), is("baeldung.base"));
With this, we created a normal module but in case we want to create an open module or an automatic one, we can respectively use the newOpenModule() or the newAutomaticModule() method.
通过这个,我们创建了一个普通的模块,但如果我们想创建一个开放的模块或一个自动的模块,我们可以分别使用newOpenModule()或newAutomaticModule()方法。
3.3. Classifying a Module
3.3.对一个模块进行分类
A module descriptor describes a normal, open, or automatic module.
一个模块描述符描述了一个正常的、开放的或自动的模块。
Thanks to the method available within the ModuleDescriptor, it’s possible to identify the type of the module:
多亏了ModuleDescriptor中的方法,才可以确定模块的类型。
ModuleDescriptor moduleDescriptor = javaBaseModule.getDescriptor();
assertFalse(moduleDescriptor.isAutomatic());
assertFalse(moduleDescriptor.isOpen());
3.4. Retrieving Requires
3.4.检索要求
With a module descriptor, it’s possible to retrieve the set of Requires, representing the module dependencies.
有了模块描述符,就可以检索到代表模块依赖关系的Requires集合。
This is possible using the requires() method:
这可以通过requires()方法实现。
Set<Requires> javaBaseRequires = javaBaseModule.getDescriptor().requires();
Set<Requires> javaSqlRequires = javaSqlModule.getDescriptor().requires();
Set<String> javaSqlRequiresNames = javaSqlRequires.stream()
.map(Requires::name)
.collect(Collectors.toSet());
assertThat(javaBaseRequires, empty());
assertThat(javaSqlRequiresNames, hasItems("java.base", "java.xml", "java.logging"));
All modules, except java.base, have the java.base module as a dependency.
除了java.base之外,所有模块都将java.base模块作为依赖关系。
However, if the module is an automatic module, the set of dependencies will be empty except for the java.base one.
然而,如果该模块是一个自动模块,那么除了java.base以外的依赖关系集将是空的。
3.5. Retrieving Provides
3.5.检索提供
With the provides() method it’s possible to retrieve the list of services that the module provides:
通过provides()方法,可以检索到模块所提供的服务列表。
Set<Provides> javaBaseProvides = javaBaseModule.getDescriptor().provides();
Set<Provides> javaSqlProvides = javaSqlModule.getDescriptor().provides();
Set<String> javaBaseProvidesService = javaBaseProvides.stream()
.map(Provides::service)
.collect(Collectors.toSet());
assertThat(javaBaseProvidesService, hasItem("java.nio.file.spi.FileSystemProvider"));
assertThat(javaSqlProvides, empty());
3.6. Retrieving Exports
3.6.检索出口
Using the exports() method, we can find out if the modules exports packages and which in particular:
使用exports()方法,我们可以找出模块是否导出了软件包,特别是哪些。
Set<Exports> javaSqlExports = javaSqlModule.getDescriptor().exports();
Set<String> javaSqlExportsSource = javaSqlExports.stream()
.map(Exports::source)
.collect(Collectors.toSet());
assertThat(javaSqlExportsSource, hasItems("java.sql", "javax.sql"));
As a special case, if the module is an automatic one, the set of exported packages will be empty.
作为一种特殊情况,如果该模块是一个自动模块,那么导出的包的集合将是空的。
3.7. Retrieving Uses
3.7.检索用途
With the uses() method, it’s possible to retrieve the set of service dependencies of the module:
通过uses()方法,可以检索到模块的服务依赖集。
Set<String> javaSqlUses = javaSqlModule.getDescriptor().uses();
assertThat(javaSqlUses, hasItem("java.sql.Driver"));
In case the module is an automatic one, the set of dependencies will be empty.
如果该模块是一个自动模块,那么依赖关系集将是空的。
3.8. Retrieving Opens
3.8.检索打开的文件
Whenever we want to retrieve the list of the open packages of a module, we can use the opens() method:
每当我们想检索一个模块的开放包列表时,我们可以使用opens()方法。
Set<Opens> javaBaseUses = javaBaseModule.getDescriptor().opens();
Set<Opens> javaSqlUses = javaSqlModule.getDescriptor().opens();
assertThat(javaBaseUses, empty());
assertThat(javaSqlUses, empty());
The set will be empty if the module is an open or an automatic one.
如果该模块是一个开放的或自动的模块,该集合将是空的。
4. Dealing With Modules
4.与模块打交道
Working with the Module API, other than reading information from the module, we can update a module definition.
使用Module API,除了从模块中读取信息外,我们还可以更新模块定义。
4.1. Adding Exports
4.1.添加出口
Let’s see how we can update a module, exporting the given package from a given module:
让我们看看我们如何更新一个模块,从一个给定的模块导出给定的包。
Module updatedModule = module.addExports(
"com.baeldung.java9.modules", javaSqlModule);
assertTrue(updatedModule.isExported("com.baeldung.java9.modules"));
This can be done only if the caller’s module is the module the code is a member of.
只有当调用者的模块是代码的成员模块时,才能做到这一点。
As a side note, there are no effects if the package is already exported by the module or if the module is an open one.
顺便提一下,如果软件包已经被模块导出,或者模块是一个开放的模块,则没有影响。
4.2. Adding Reads
4.2.添加读数
When we want to update a module to read a given module, we can use the addReads() method:
当我们想更新一个模块来读取一个给定的模块时,我们可以使用addReads()方法。
Module updatedModule = module.addReads(javaSqlModule);
assertTrue(updatedModule.canRead(javaSqlModule));
This method does nothing if we add the module itself since all modules read themselves.
如果我们添加模块本身,这个方法没有任何作用,因为所有的模块都是自己读的。
In the same way, this method does nothing if the module is an unnamed module or this module already reads the other.
同样,如果该模块是一个未命名的模块,或者这个模块已经读取了另一个模块,那么这个方法就没有任何作用。
4.3. Adding Opens
4.3.添加开局
When we want to update a module that has opened a package to at least the caller module, we can use addOpens() to open the package to another module:
当我们想更新一个已经向至少是调用者模块打开包的模块时,我们可以使用addOpens()来向另一个模块打开该包。
Module updatedModule = module.addOpens(
"com.baeldung.java9.modules", javaSqlModule);
assertTrue(updatedModule.isOpen("com.baeldung.java9.modules", javaSqlModule));
This method has no effect if the package is already open to the given module.
如果软件包已经对给定的模块开放,这个方法就没有效果。
4.4. Adding Uses
4.4.增加用途
Whenever we want to update a module adding a service dependency, the method addUses() is our choice:
每当我们想更新一个添加服务依赖的模块时,方法addUses()是我们的选择。
Module updatedModule = module.addUses(Driver.class);
assertTrue(updatedModule.canUse(Driver.class));
This method does nothing when invoked on an unnamed module or an automatic module.
当对一个未命名的模块或自动模块调用该方法时,不做任何事情。
5. Conclusion
5.结论
In this article, we explored the use of the java.lang.Module API where we learned how to retrieve information of a module, how to use a ModuleDescriptor to access additional information regarding a module and how to manipulate it.
在这篇文章中,我们探索了java.lang.Module API的使用,我们学习了如何检索一个模块的信息,如何使用ModuleDescriptor来访问关于一个模块的额外信息,以及如何操作它。
As always, all code examples in this article can be found over on GitHub.
一如既往,本文中的所有代码示例都可以在GitHub上找到over。