Project Jigsaw实现了什么:正如我之前解释的,Jigsaw项目解决了整个Java API用作单个整体代码库的问题。 以下几点突出了主要优点。
1.依赖关系图 :Jigsaw提供了一种方法,可以唯一地标识特定的代码库,还可以声明代码库对其他代码库的依赖关系。 这将为一组特定的类创建一个完整的依赖图。 例如,您要编写一个依赖于Apache BCEL库的程序。 到目前为止,您还没有办法在代码本身中表达此要求。 使用Jigsaw,您可以在代码本身中表达此要求,从而允许工具解决此依赖性。
2.同一代码的多个版本 :假设您编写了一个同时依赖libray A和库B的程序。现在假设库A依赖于库C的版本1.0,库B依赖于库C的版本2.0。在当前的Java中在运行时,如果不创建自定义类加载器的复杂层次结构,就无法同时使用库A和B,即使这在所有情况下都行不通。 在Jigsaw成为Java的一部分之后,这就不成问题了,因为一个类将只能看到其依赖类的版本,这些依赖类是类容器模块所需的模块版本的一部分。 也就是说,由于模块A依赖于模块C的版本1.0,而模块B依赖于模块C的版本2.0,因此Java运行时可以确定模块A或模块可以看到模块C中的类的哪个版本。 B.这类似于OSGi项目。
3. Java平台本身的模块化 :当前的Java平台API庞大,并非每种情况下它的所有部分都相关。 例如,旨在运行Java EE服务器的Java平台不必实现Swing API,因为这没有任何意义。 同样,嵌入式环境可以精简一些不太重要的API(对于嵌入式),例如编译器API,以使其更小,更快。 在当前的Java平台下,这是不可能的,因为任何经过认证的Java平台都必须实现所有API。 拼图将提供一种仅实现与特定平台相关的API集的一部分的方法。 由于模块可以显式声明其对任何特定Java API模块的依赖关系,因此只有在平台具有该模块所要求的模块的实现时,该模块才会运行。
4.与OS本机安装集成 :由于模块系统与现代操作系统中当前可用于安装程序和库的模块系统非常相似,因此可以将Java模块与这些系统集成。 这些实际上不在Jigsaw项目本身的范围内,但鼓励OS供应商启用它,并且他们很可能会这样做。 例如,可以轻松地增强基于Redhat的linux系统中可用的基于rpm的存储库系统和基于Debian的linux系统中可用的基于apt的存储库系统,以支持java模块系统。
5.模块入口点 :Java模块可以指定入口点类,就像jars可以指定它一样。 运行模块时,将调用入口点的main方法。 现在,由于操作系统现在可以安装Java模块并且可以执行Java模块,因此它与安装操作系统的本机程序非常相似。
5.效率 :目前,每次运行JVM时,它都会验证程序运行期间加载的每个单个类的完整性。 这需要花费大量时间。 此外,还可以从OS文件系统单独访问类。 由于可以在运行之前安装模块,因此安装本身现在可以包含验证步骤,从而无需在运行时验证类。 这将导致相当大的性能改进。 而且,模块系统可以以其自己的优化方式存储类,从而进一步提高性能。
6.模块抽象 :可以为特定模块提供抽象。 说模块A依赖于模块X。现在,模块D可以提供模块X,从而提供其实现。 例如,Apache Xerces模块将要提供jdk.jaxp模块,并且能够满足jdk.jaxp的依赖关系要求。
模块化代码库的基础:上面的所有讨论都非常模糊,没有模块化代码库及其用法的真实示例。 模块化代码库可以是单个模块或多个模块。 对于单个模块,我们需要启用模块是在任何包外部的源路径的基础上创建一个名为module-info.java的文件。 module-info.java文件是以特殊语法编写的特殊Java文件,旨在声明模块信息。 以下是此类mdoule-info.java的示例。
module com.a @ 1.0{
requires com.b @ 1.0;
class com.a.Hello;
}
在这种情况下,该模块名为com.a,并且它对com.b有依赖性。 它还声明了一个入口点com.a.Hello。 请注意,虽然这可能是最佳做法,但并不要求程序包结构必须在模块名后加上模块名。
现在您可能会想,如果它是单个模块模式,那么为什么要依赖于另一个模块,却不能使它成为两个模块。 请注意,即使只有一个显式声明依赖模块,对所有Java API模块也存在隐式依赖。 如果没有将Java API模块明确声明为依赖项,则将全部包含。 它仍然是单个模块的唯一原因是com.b必须在模块库中以二进制形式可用。 同时编译多个模块时的多模块。 在单个模块中编译源代码就像我们编译非模块化源代码一样简单。 唯一的区别是module-info.java将出现在源根目录中。
多模块源:如果源包含多个模块,则必须为它们提供目录结构。 它非常简单。 特定模块下的源必须保存在模块名称的目录中。 例如,模块com.a中com.a.Hello类的源必须保存在[source-root] /com.a/com/a/Hello.java中,而模块-info.java必须保留在目录[source-root] /com.a中
编译多模块源:为此,让我们考虑一个编译两个模块com.a和com.b的示例。 让我们首先看一下目录结构。 如下:
classes
src
|--com.a
| |--module-info.java
| |--com
| |--a
| |--Hello.java
|--com.b
|--module-info.java
|--com
|--b
|--Printer.java
com.a中module-info.java的代码将像这样。
module com.a @ 1.0{
requires com.b @ 1.0;
class com.a.Hello;
}
com.b中的module-info.java
module com.b @ 1.0{
exports com.b;
}
com.b / com / b中的Printer.java
package com.b;
public class Printer{
public static void print(String toPrint){
System.out.println(toPrint);
}
}
com.a / com / a中的Hello.java
package com.a;
import com.b.Printer;
public class Hello{
public static void main(String [] args){
Printer.print("Hello World!");
}
}
这些代码很容易解释,我们尝试使用模块com.a中com.a.Hello类中的模块com.b中的com.b.Printer类。 为此,com.a module-info.java必须使用Requires关键字将com.b声明为依赖项。 我们正在尝试在classes目录中创建输出类文件。 下面的javac命令可以做到这一点。
javac -d classes -modulepath classes -sourcepath src `find src -name '*.java'`
请注意,我们在backquotes(`)中使用了find命令,以便该命令的输出将包含在文件列表中。 这将在linux和unix环境中工作。 在其他情况下,我们可以简单地输入文件列表。
编译后,classes目录将具有类似的类结构。 现在,我们可以使用jmod命令安装模块。
jmod create -L mlib
jmod install -L mlib classes com.b
jmod install -L mlib classes com.a
我们首先创建了一个模块库mlib,并将模块安装在该库中。 通过在jmod中不为install命令指定-L选项,我们也可以使用默认库。
现在我们可以简单地使用以下命令运行模块com.a
java -L mlib -m com.a
在这里我们也可以使用默认模块。 还可以创建可直接安装的可分发模块软件包(相当于今天的分发机制中的jar)。 例如,以下将为com.a创建com.a@1.0.jmod
jpkg -m classes/com.a jmod com.a
我试图在即将发布的Java版本中概述模块基础结构。 但是项目Jigsaw每天都在进行修改,并且最终可能完全不同。 但是,预计基本概念将保持不变。 整个模块的概念更加复杂,我将在下一篇文章中详细介绍。
参考: Java 8中的烹饪方法–来自JCG合作伙伴 Debasish Ray Chawdhuri的Jigsaw项目 ,位于Geeky Articles博客上。
翻译自: https://www.javacodegeeks.com/2012/05/whats-cooking-in-java-8-project-jigsaw.html