Java 模块化系统初探
作者:网络转载 发布时间:[ 2015/9/28 16:30:36 ] 推荐标签:编程语言 测试开发技术
Java 模块化系统自提出以来经历了很长的时间,直到 2014 年晚些时候才终以 JSR(JSR-376) 定稿,而且这个部分有可能在 Java 9 中出现。但是一直以来都没有可以使用的原型。9 月 11 日,OpenJDK 发布的早期构建版本终于包含了 Jigsaw 项目。
昨天,我和同事 Paul Bakker 在 JavaZone 上对于 Java 模块化系统进行了讨论。整个讨论都建立在JSR-376 需求文档以及身边一些珍贵的信息上。在年初提出举行这个报告的时候,我们曾深信不疑地认为在这个会上我们能够展示一个原型,但是事情却没有按预想的那样发展。现在的情况是,这个原型将在我们的报告结束之后发布。这也意味着,报告中的一些内容已经有点过时了,但是主要的思想还是很有新意的。如果你对 Java 模块化系统方案一无所知的话,建议你在阅读这篇文章之前先去看一下我们的报告。我们的报告介绍了现在的方案,并进一步与 OSGi 进行了比较。
为什么要使用模块?
什么是模块?我们为什又需要它们?如果希望有一个深入的讨论,请阅读“State of the module system”或者看一下我们的报告。对这块还不是很了解的人来说,这里有Cliff 的注释版本。
我们都知道 Java 有 jar 文件。但是,事实上这些都只是包含一些class(类)的压缩文件,这些 jar 包内部都是一些 package (包)。当你利用一些不同的 jar 包来运行应用程序的时候(复杂一点的程序也适用),你需要把它们放到指定的类路径中。然后默默祈祷。因为没有有效的工具来帮助你知道,你是否已经把应用所需要的 jar 包都放入类路径中了。或者有可能你在不经意间将同样的类文件(在不同的 jar 包中)都放入了类路径中。类路径灾难(类似于 DLL 灾难)是真实存在的。这会导致运行时出现糟糕的状况。同时,在运行时我们也无法得知 jar 中包含哪些类。从 JRE 角度来说只知道有一堆类文件。事实上 jar 包之间是相互依赖的,但目前还不能把这种依赖关系记录到数据文件中去。理想的情况是,你可以隐藏 jar 包中类文件具体的实现,只是提供一些公共的 API 。在 Java 中提出模块化系统是为了解决这些问题的:
模块成为首先要考虑的部分,它能够分装实现细节并且只暴露需要的接口。
模块准确地描述了他们能够提供的接口,以及他们的需要部分(依赖)。由此,我们可以在开发的过程中弄清和处理依赖关系。
模块系统极大地提升了大型系统的可维护性、可靠性、安全性。至少 JDK 本身还缺少这样的系统。通过这样的模块系统,模块图能够自动地构建。这个图只包括了你的应用程序运行时所须要的模块。
安装 JDK9 预览版
如果你想亲自尝试编写示例代码,你需要安装包含 Jigsaw 原型的 JDK9 早期构建版本。在 OSX 上,你需要解压文档,然后把解压出来的目录移动到 Library/Java/JavaVirtualMachines/ 下。然后你需要设置环境变量,将 JAVA_HOME 环境变量指向 JDK9 的目录。我使用了非常好用的setjdk 脚本,通过它可以在命令窗口中实现 Java 安装的命令切换。你很有可能不愿意使用这个早期构建版本作为你的 Java 安装版本。你可以通过 java -version 来确认安装完成。输出如下面所示:
java version "1.9.0-ea"
Java(TM) SE Runtime Environment (build 1.9.0-ea-jigsaw-nightly-h3337-20150908-b80)
Java HotSpot(TM) 64-Bit Server VM (build 1.9.0-ea-jigsaw-nightly-h3337-20150908-b80, mixed mode)
只要输出中包含 Jigsaw ,你可以继续了。文章后面的示例代码可以去 https://github.com/sandermak/jigsaw-firstlook 下载。
一个简单的例子
你仍旧可以通过类、jar包以及类路径这样“传统方式”的方式来使用 JDK9 。但是明显地我们想要采用模块的方式。所以我们将创建一个包含两个模块的工程:模块一使用了模块二中的代码。
首先要做的是,构建我们的工程并把两个模块很好地区分开来。然后,模块中需要以 module-info.java 文件的形式添加元数据。我们的示例构建如下:
src
module1
module-info.java
comtestTestClassModule1.java
module2
module-info.java
commoretestTestClassModule2.java
接着,我们将介绍 package (包)层顶上的一层(module1、 module2),这部分你在之前已经构建好了。在这些“模块目录”中,可以看到 module-info.java 文件在根目录下。此外请注意,这两个类都是在显示命名的包中的。
请看 TestClassModule1 的代码:
package com.test;
import com.moretest.TestClassModule2;
public class TestClassModule1 {
public static void main(String[] args) {
System.out.println("Hi from " + TestClassModule2.msg());
}
}
看起来很普通对吧?这里并没有涉及模块,而是导入了 TestClassModule2 ,主函数之后会去调用其中的 msg() 方法。
package com.moretest;
public class TestClassModule2 {
public static String msg() {
return "from module 2!";
}
}
到目前为止,module-info.java 还是空的。
对 Java 模块进行编译
现在进行下一步:编译我们的模块,并关联源文件。为了做这项工作,我们将介绍一个新的 javac 编译参数:
javac -modulesourcepath src -d mods $(find src -name '*.java')
使用上面语句时,我们假设命令程序已经处于 src 文件夹的上级目录中了。-modulesourcepath 参数会让 javac 从传统编译模式进入模块模式。-d 标记指出了编译好的模块的输出目录。javac 将以非打包文件的形式输出这些模块。如果我们这之后想以 jars 的形式使用这些模块的话,需要一个单独的步骤。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11