前言
JPMS(Java Platform Module System) Java平台模块系统是Java 9的主要增强。它也被称为Jigsaw项目。在本文中,我们将简单学习模块,以及在将来开始编写模块化代码时,编程风格将如何变化。
正文
什么是模块?
在任何编程语言中,模块(类似于包)都是包含代码的构件,其中包含描述模块及其与其他模块的关系的元数据。理想情况下,这些构件从编译时一直到运行时都是可识别的。任何应用程序通常都是多个模块的组合,这些模块一起工作以执行业务目标。
在应用程序架构方面,模块应该表示特定的业务功能。对于该功能,它应该是自给自足的,并且应该只公开使用模块功能的接口。为了完成它的任务,它可能依赖于其他模块,它应该显式地声明这些模块。
因此,简而言之,一个模块应该遵循三个核心原则:
强大的封装(Strong Encapsulation)
封装意味着隐藏实现细节,这些细节对于正确使用模块并不重要。其目的是封装的代码可以自由改变而不影响模块的用户。
稳定的抽象(Stable Abstraction)
抽象有助于使用接口(即公共api)公开模块功能。任何时候,如果想要更改模块代码中的业务逻辑或实现,更改对模块用户都是透明的。
显式的依赖关系(Explicit dependencies)
模块也可以依赖于其他模块。这些外部依赖必须是模块定义本身的一部分。模块之间的这些依赖关系通常用图表示。一旦您看到应用程序级别的图,您将更好地理解应用程序的体系结构。
Java 9模块化介绍
在Java 9之前,我们有“包(packages)”来根据业务功能对相关类进行分组。除了包之外,还有“访问修饰符”来控制哪些是可见的,哪些是隐藏在其他类或包中的。到目前为止,它运行得很好。Java对封装和抽象提供了强大的支持。
但是,显式依赖关系是事情开始崩溃的地方。在java中,依赖项是用“import”语句声明的;但是它们是严格的“编译时”构造。一旦代码被编译,就没有明确的机制来声明它的运行时依赖关系。事实上,java运行时依赖项解析是一个非常有问题的领域,因此专门创建了一些工具来解决这个问题,例如gradle或maven。此外,很少有框架捆绑它们的完整运行时依赖项,例如Spring boot项目。
有了新的Java 9模块,我们将能够更好地编写结构良好的应用程序。这种增强分为两个方面:
- 模块化JDK本身。
- 提供一个模块系统供其他应用程序使用。
Java 9模块系统有一个“java.base”模块。它被称为基模块。它是一个独立的模块,不依赖于任何其他模块。默认情况下,所有其他模块都依赖于“java.base”。
在Java 9中,模块帮助我们封装包并管理依赖项。所以通常情况下,
- 类是字段和方法的容器
- 包是类和接口的容器
- 模块是包的容器
如果我们不知道要查找的具体内容,那么我们不会感觉到普通代码和模块化代码之间的任何主要区别。如:
- 模块通常只是一个jar文件,在根目录下有一个
module-info.class
文件。 - 要使用模块,请将jar文件包含到
modulepath
中,而不是classpath
中。添加到classpath
中的模块jar文件是普通的jar文件,而module-info.class
文件将被忽略。
如何编写模块化代码
在阅读了所有上述概念之后,让我们看看模块化代码是如何在现实中编写的。我使用Netbeans IDE是因为它对Java 9有很好的早期支持(到今天为止)。
创建Java模块项目
创建一个Java模块项目,命名为JavaAppOne
。
创建Java模块
我们向这个项目中添加两个模块,如下:
我向项目中添加了helloworld
和test
模块,它们的结构如下:
相关代码如下:
/helloworld/module-info.java
1 | module helloworld { |
HelloWorldApp.java
1 | package com.howtodoinjava.demo; |
/test/module-info.java
1 | module test { |
TestApp.java
1 | package com.test; |
到目前为止,模块是独立的。现在假设,我们想在TestApp类中使用HelloWorldApp.sayHello()
方法。如果我们尝试在不导入模块的情况下使用该类,我们将得到编译时错误“package com.howtodoinjava.demo is not visible”。
导入模块信息
为了能够导入HelloWorldApp
,我们必须首先从helloworld
模块导出“com.howtodoinjava.demo”包,然后在test
模块中包含helloworld
模块。
1 | module helloworld { |
在上面的代码中,require
关键字表示依赖项,exports
关键字标识可以导出到其他模块的包。只有当一个包被显式导出时,才能从其他模块访问它。模块内未导出的包在默认情况下无法从其他模块访问。
现在我们可以在TestApp
类中使用HelloWorldApp
类了。
1 | package com.test; |
模块关系图如下:
从Java 9开始,
public
意味着只对该模块内的所有其他包公开。只有在导出包含公共类型的包时,其他模块才能使用它。
结语
模块化应用程序有许多优点,当我们遇到具有非模块化代码库的应用程序时,我们会更加欣赏这些优点。模块化并不是什么灵丹妙药,但它是一种体系结构原则,如果应用得当,可以在很大程度上避免项目依赖混乱问题。
有了JPMS, Java向成为模块化语言迈出了一大步。这个决定是对是错,只有时间能证明。第三方库和框架如何适应和使用模块系统将会很有趣。以及它将如何影响开发工作,由我们来见证。