java9 模块
Java 9的主要创新是引入了模块。 关于此功能的讨论很多,发布日期被推迟了几次以正确完成所有操作。 今天,我们将讨论模块的机制,以及Java 9总体带来的好处。 该帖子基于IntexSoft Java开发人员Sergei Malkevich的报告。
拼图项目
为了实现此版本的Java中的模块,分配了一个全新的项目-Project Jigsaw,其中包括几个JEP和JSR。
对于那些喜欢官方文档的人,您可以在这里了解有关每个JEP的更多信息。
拼图计划始于2005年:首先发布了JSR 277,然后在2008年开始了该项目的实际工作。 它仅在2017年发布。因此,以正确的方式完成Java模块花了将近10年的时间。 实际上,这强调了模块实施过程中的全部工作和所做的更改。
项目目标:
- 促进大型应用程序和库的开发;
- 总体上提高Java SE的安全性,尤其是JDK的安全性;
- 提高应用程序性能;
- 使Java SE和JDK缩小规模以在小型设备中使用,以免消耗过多的内存;
- 杰尔·赫尔
Java 9更新
在版本9之前, JDK和JRE是整体的 。 每个发行版的大小都会增加。 Java 8已经占据了数百Mb,开发人员每次都必须“携带所有这些东西”才能运行Java应用程序。 仅rt.jar一项大约需要60 Mb。 好了,在这里我们还可以增加启动速度慢和占用大量内存的功能。 因此,Java 9可以在这里提供帮助。
JDK 9引入了模块系统,即JDK分为73个模块。 每个新版本都会为我们带来更多这些模块。 在11版本中,该数字接近100。这种分离允许开发人员创建Jlink工具来创建自定义JRE,该JRE仅包含应用程序真正需要的模块。 因此,一个简单的应用程序和一些带有最少(或很少)模块集的自定义JRE最终可以容纳20 Mb,这是一个好消息。
您可以在此处查看模块列表。
在Java 9中,JDK结构已更改:现在,它与JRE结构相同。 如果以前的JDK包含带有bin和重复文件的JRE文件夹,那么现在所有内容如下所示:
模组
实际上是什么模块? 模块是软件包和资源聚合的新层次,或者如开发人员所说:
...一组唯一命名的,可重用的相关软件包,以及资源和模块描述符 。
模块以JAR文件的形式提供,其中包含软件包和模块描述符-module-info.java。 module-info.java文件包含:名称,依赖项,公共包,已使用和提供的服务,反射权限。
模块描述符的示例:
module java.sql {
requires transitive java.logging;
requires transitive java.transaction.xa;
requires transitive java.xml;
exports java.sql;
exports javax.sql;
uses java.sql.Driver;
}
module jdk.javadoc {
requires java.xml;
requires transitive java.compiler;
requires transitive jdk.compiler;
exports jdk.javadoc.doclet;
provides java.util.spi.ToolProvider with
jdk.javadoc.internal.tool.JavadocToolProvider;
provides javax.tools.DocumentationTool with
jdk.javadoc.internal.api.JavadocTool;
provides javax.tools.Tool with
jdk.javadoc.internal.api.JavadocTool;
}
首先是module
关键字,后跟jdk.javadoc
包的名称,该包依赖于另一个java.xml
包,并且可传递地依赖于其他包。
让我们仔细看一下关键字:
-
requires
指定当前模块所依赖的模块;
-
requires transitive
指定传递依赖:如果模块m1可传递依赖于模块m2 ,并且我们有一些依赖于m1的第三个模块mX ,则模块mX也可以访问m2;
-
requires static
指定仅编译时依赖项;
-
exports
指定其他模块应可访问的模块软件包(不包括“子软件包”);
-
export
exports…to…
允许我们限制访问:export
com.my.package.name
to
com.specific.package
; 也就是说,我们只能为另一个模块的其他一些软件包打开对模块软件包的访问权限;
-
uses
指定模块uses
的服务:
uses java.sql.Driver;
在这种情况下,我们指定所使用服务的接口。
-
provides
指定模块provides
的服务:
provides javax.tools.Tool with
jdk.javadoc.internal.api.JavadocTool;
首先,我们把接口- javax.tools.Tool
,后with
-实施。
有关服务的更多信息
假设我们连接了几个实现抽象服务的模块MyService
。 组装应用程序时,我们可以通过将所需的服务实现模块放到--module-path
来决定要使用的服务实现:
Iterable <MyService> services =
ServiceLoader.load(MyService.class);
因此,返回的Iterator包含MyService
接口的实现列表。 实际上,它将包含在--module-path
的模块中找到的所有实现。
为什么要引入服务? 他们需要显示如何使用我们的代码。 因此,这全都与语义角色有关。 另外,模块化是关于封装和安全性的,因为我们可以将实现private
并排除未经授权的反射访问的可能性。
使用服务的另一个选择是插件的相当简单的实现。 我们可以为我们的应用程序实现插件接口,并连接模块以使用它们。
让我们回到声明
在Java 9之前,可以使用反射来访问几乎所有内容,并且我们可以做任何我们想做的事情。 但是正如已经提到的,版本9允许我们保护应用程序免受“非法”反射访问。
我们可以通过声明open
来允许对模块进行全反射访问:
openmodule my. module { }
或者,我们可以使用opens
公开特定的包:
module my. module { opens com.my.coolpackage;
}
也可以使用opens…to
,从而将特定的软件包打开到特定的模块。
模块类型
拼图项目将模块分类如下:
- 系统模块 — Java SE和JDK模块。 您可以使用java
--list-modules
命令找到完整列表。 - 应用程序模块 -由我们编写的应用程序模块 ,以及应用程序使用的依赖项(来自第三方库)。
- 自动模块 -这些模块是Java会根据JAR文件自动创建的具有开放访问权限的模块。 假设我们想以模块化模式运行我们的应用程序,但是它使用了一些库。 在这种情况下,我们将JAR文件放在
--module-path
,Java会自动创建一个模块,该模块的名称继承自JAR文件的名称。 - 未命名模块 -从加载到
--class-path
所有JAR文件中自动生成的未命名模块。 这是一个万能的模块,用于与以前编写的Java代码向后兼容。
类路径与模块路径
有了模块,出现了一个新概念-module -path 。 实际上,这是我们都知道的类路径,但对于模块而言。
启动模块化应用程序如下所示:
在“经典模式”下,我们指定选项和主类的完整路径。 如果要使用模块,则还要指定-m
或-module
参数,这表明我们将运行模块。 也就是说,我们自动将我们的应用程序置于“模块化模式”。 然后,我们指定模块名称和从模块到主类的路径。
另外,除了我们常用的经典-cp
和--class-path
参数外,我们还添加了新的-p
和--module-path
参数,这些参数指示应用程序中使用的模块的路径。
开发人员经常会切换到Java 9+,因为他们认为他们将必须使用模块。 尽管实际上,我们可以运行应用程序而无需为模块添加参数,而仅使用Java 9的其他新功能。
杰尔·赫尔
简而言之,Jar Hell是什么?
例如,我们的应用程序依赖于Library X和Library Y。 而且,这两个库都依赖于库Z ,但是依赖于不同的版本: X依赖于版本1 , Y依赖于版本2 。
如果版本2向后兼容版本1是可以的 ,那么我们就不会有问题。 但是,如果不是这样,显然,我们将发生版本冲突,这意味着同一类加载器无法将同一库加载到内存中。
开发人员如何克服这种情况? 自从最早的Java以来,开发人员就一直使用标准方法,例如:exclude或Maven的插件,它们重命名了库的根包。 有时开发人员正在寻找库X的不同版本来查找兼容选项。
实际上,最初的Jigsaw原型假定该模块可能具有版本,并允许不同的ClassLoader下载不同的版本。 后来那个想法被删除了。 结果,我们许多人等待的“银弹”没有解决。
但是,开发人员仍然可以立即解决此类问题。 在Java 9中 ,禁止使用拆分包。 这些程序包分为几个模块。 也就是说,如果在一个模块中有com.my.coolpackage
,则不能在同一应用程序的另一个模块中使用它。
如果我们使用包含相同软件包的模块来运行应用程序,我们将崩溃。 这一小改进消除了与拆分包下载有关的不可预测行为的可能性。
除了模块本身之外,还有拼图层机制,这也有助于应付Jar Hell。
拼图层可以定义为某些本地模块系统。 这里值得注意的是,上面提到的Split包仅在一个拼图层中被禁止。 具有相同包装的模块有放置的位置,但是它们必须属于不同的层。
看起来像这样:
当应用程序启动时,将创建一个Boot层,其中包括Bootstrap加载的平台模块,Extension loader加载的其他平台模块以及Application loader加载的应用程序模块。
我们可以随时创建自己的图层,并在那里放置不同版本的模块而不会出现任何问题。
这是Nikita Lipsky的详细演讲,以了解有关该主题的更多信息: 使用拼图图层逃避Jar Hell
总结一下
Java 9的模块为我们开辟了新的可能性,而当今对库的支持非常有限。 当然,人们会运行Spring,Spring Boot等。 但是大多数库都没有切换到完全使用模块。 显然,这就是为什么所有这些更改都遭到了技术社区的怀疑。 模块为我们提供了新的机会,但需求问题仍然悬而未决。
最后,这是要学习的有用内容的列表:
先前发布在 https://www.intexsoft.com/blog/post/java9-modules.html
翻译自: https://hackernoon.com/understanding-java-9-modules-7f573vfe
java9 模块