java 多模块项目 包路径冲突_Java模块系统新特性快速上手指南

4d42b002b4cc12bd572a42be7a8fac4e.png

Java平台模块系统

0.导引

Java发展的越来越无所不能了,从Java 9+开始而引入的平台模块系统,就是一个很大的新特性,使组织代码变得更容易,也更易于功能解耦化和微型化。本文是一个使用Java模块的简短快速上手指南。

在Java 9之前,Java的顶级代码组织元素一直是包。从Java 9开始变了:在包package的上面是模块module。模块将相关的包收集聚合在一起。

Java平台模块系统(JPMS,Java Platform Module System)是一种代码级结构,其不会改变将Java打包到JAR文件中的事实。本质上讲,所有内容仍然打包在JAR文件中。模块系统通过结合 module-info.java文件,添加了jar可以使用的新的高级描述符。

大型应用程序和组织机构,可以利用模块化来更好地组织代码。其实现在每个人都在使用着模块,因为JDK及其类现在已经模块化了。

1.为什么Java需要模块

JPMS是Jigsaw工程的成果,该工程的目标如下:

  • l 使开发人员更容易组织大型应用程序和库;
  • l 改进平台和JDK本身的结构和安全性;
  • l 提高应用程序性能;
  • l 对于较小的设备,更好地处理平台的分解面。

值得注意的是,JPMS是一个SE(标准版)特性,因此会从头影响Java的各个方面。尽管如此,这种结构改变后的设计,当在从Java 8迁移到Java 9时,最大程度的允许功能代码无需修改即可运行。这里有一些例外,我们将在稍后的概述中进行说明。

模块背后的主要思想是允许相关包的集合对模块是可见的,而对模块的外部使用者隐藏一些要素。换句话说,模块是允许另一种级别的封装。

2.类路径和模块路径

到目前为止,在Java中,类路径一直是运行着的程序的可用内容的底线。尽管类路径服务于此目的,并且很容易理解,但它最终将是一个大的、无差异的桶,所有依赖项都被放置在其中。

模块路径在类路径之上添加了一个级别。它充当包的容器,并决定哪些包对应用程序可用。

3.JDK中的模块

JDK本身现在是由模块组成的。让我们从JPMS的细节开始。

如果您的系统上有JDK,那么您也有源代码。如果您不熟悉JDK以及如何获得它,请参阅@牛旦IT课堂的这篇文章:JDK 、JRE 与JVM之间的关系及区别

在JDK安装目录中有一个/lib目录。在该目录中有一个src.zip文件。将其解压缩到/src目录中。

查看/src目录,并导航到/java.base目录。在那里您将找到module-info.java文件,然后打开它。

在头部的Javadoc注释之后,您会发现一个命名为module java.base的部分,其随后有一系列exports行。这里我们不详细讨论格式,因为它相当深奥。详情请点击这里的连接(https://blog.joda.org/2017/04/java-se-9-jpms-module-naming.html)——有机会我再详解之。

您可以看到许多熟悉的Java包,比如java.io,是从java.base模块导出的。这是集合或汇聚相关包的模块本质。

exports的另一面是requires指令。这允许被定义的模块被引入(需要)到某个模块。

在对模块运行Java编译器时,可以类似于类路径的方式指定模块路径。这样就可以解决依赖问题。

4.创建模块化Java项目

让我们来看看模块化Java项目是如何构建的。我打算创建一个有两个模块的小程序,一个提供依赖项,另一个使用依赖项并导出一个可执行的主类。

在文件系统中某地方创建一个新目录(模块名),叫它/com.inewday.mod1。按照约定,Java模块位于与模块同名的目录中。

14f7e517dd891af870048e977fe70efe.png

现在,在这个目录中,创建一个module-info.java文件。在其中添加清单1中的内容。

清单 1: com.inewday.mod1/module-info.java

module com.inewday.mod1 {  exports com.inewday.package1;}

请注意,模块和它导出的包是不同的名称。此处我定义了导出包的模块。

现在在这个路径上创建一个文件,在目录中包含module-info.java文件:/com.inewday.mod1/com/inewday/package1。将文件命名为Name.java。将清单2的内容写入其中。

清单2 Name.java

package com.inewday.package1; public class Name {  public String getIt() {    return "Java Module World";  }}

清单2将成为我们所依赖的类、包和模块。

现在我来创建另一个与/com.inewday.mod1平行的目录,并叫做/com.inewday.mod2。在这个目录中,我来创建一个module-info.java模块定义,它导入我已经创建的模块,如清单3所示。

清单 3: com.inewday.mod2/module-info.java

module com.inewday.mod2 {  requires com.javaworld.mod1;}

清单3不言而喻,它定义了com.inewday.mod2模块,并引入/需要com.inewday.mod1。

在/com.inewday.mod2目录下,创建类路径如下:/com.inewday.mod2/com/inewday/package2。

现在在里面添加一个名为Hello.java的文件。使用清单4中提供的代码。

清单 4 Hello.java

package com.inewday.package2; import com.inewday.package1.Name; public class Hello {  public static void main(String[] args) {    Name name = new Name();    System.out.println("Hello " + name.getIt());  }}

在清单4中,我们从定义包开始,然后导入com.inewday.package1.Name类。请注意,这些元素一如既往地发挥作用。这些模块已经改变了包在文件结构层(而不是代码层)可用的方式。

类似地,您应该对代码本身很熟悉。它只是简单地创建一个类并对其调用一个方法来创建一个经典的“hello world”雷同示例。

5.运行模块化Java示例

76c0147c3d0e6298674806ebb069a126.png

第1步:创建接收编译器输出的目录。在项目的根目录中创建一个名为/target的目录。在其内部,分别为每个模块创建一个目录:/target/com.inewday.mod1和/target/com.inewday.mod2。

第2步:编译依赖项模块,并将其输出到/target目录。在项目的根目录中,输入清单5中的命令。(假设已经安装了JDK。)

清单5:构建模块1

javac -d target/com.inewday.mod1 com.inewday.mod1/module-info.java com.inewday.mod1/com/inewday/package1/Name.java

这将导致源代码随其模块信息一起被构建。

第3步:生成依赖模块。输入清单6中所示的命令。

清单6:构建模块2

javac --module-path target -d target/com.inewday.mod2 com.inewday.mod2/module-info.java com.inewday.mod2/com/inewday/package2/Hello.java

请详细看看清单6。它将module-path参数引入到javac。这允许我们以类似于--class-path开关的方式定义模块路径。在本例中,我将传入target目录,因为清单5在该目录中输出模块1。

接下来,清单6(通过-d开关)定义了模块2的输出目录。最后给出了编译的实际主题,即模块2中包含的module-info.java文件和类。

为了运行程序,使用清单7中所示的命令。

清单7:执行模块主类

java --module-path target -m com.inewday.mod2/com.inewday.package2.Hello

--module-path开关告诉Java使用/target目录作为模块根目录,也就是说,在哪里搜索模块。-m开关是告诉Java主类在什么地方。注意,我们在完全限定类名的前面加上它的模块。

您将看到输出Hello Java Module World

6.向后兼容性

您可能很想知道如何在后Java 9世界中运行用模块化前编写的Java程序,因为以前的代码库对模块路径一无所知。答案是Java 9被设计为向后兼容,即以前代码依然可以运行在9及以后的版本中。但是,新的模块系统是一个巨大的变化,您可能会遇到问题,特别是在大型代码库中。

在Java 9上运行9以前的代码库时,您可能会遇到两种错误:一种源自您的代码库,另一种源自您的依赖。

对于源于代码库的错误,这个命令可能会有所帮助:jdeps。当指向一个类或目录时,该命令将扫描存在哪些依赖项,以及这些依赖项依赖的模块。

对于源于依赖关系的错误,您可寄希望您所依赖的包将会有更新的Java 9兼容构建版本。如果没有,你可能不得不寻找其他选择。

一个常见的错误是这样的:

How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

这是Java抱怨找不到某个类,因为它已经迁移到一个模块,而对使用方(消费代码)不可见。这里(https://stackoverflow.com/questions/43574426/how-to-resolve-java-lang-noclassdeffounderror-javax-xml-bind-jaxbexception-in-j) 描述了几种具有不同复杂性和持久性的解决方案。

同样,如果在依赖项中发现此类错误,请进行项目检查。它们可能有Java 9构建版本供您使用。

JPMS是一个相当彻底的改变,需要时间来适应。幸运的是,并不着急,因为Java 8是一个长期支持版本。

也就是说,从长远来看,旧的项目将需要迁移,而新的项目将需要明智地使用模块,以期能够利用一些模块应允的好处。

7.小结

本文简要介绍了Java中的模块系统的相关技术知识,并做了实例性演示实战操作,动手练一把,基本就能窥得JPMS的总体特性了,后续有机会再深入讲解相关技术细节。

本篇就到这里了,关注一下,分享出去吧,谢谢.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值