lostars
发布于 2018-01-02 / 1171 阅读
0

JAVA 9 新特性 — MODULE SYSTEM

Java 9正式版发布已然有小半年,带来最令人激动的消息就是模块化编程。本来打算系统的了解一下这个新特性,百度到的也就一些简单介绍和demo,或者是国外的一些文章翻译,于是谷歌到了一些官方文档学习。

Module System 的出现将整个JDK的包结构都改变了:旧版本的Java都是以jar包的形式出现,而Java 9重构了整个系统,采用的是模块化的设计。在Java 9的安装文件夹下可以看到一个叫jmods的文件夹,里面存放的都是jmod后缀的文件,这些就是Java 系统提供的模块,现在都是以模块的形式来加载和提供服务。

在Java 9里,系统都用模块来划分,模块之间相互引用和依赖。系统模块分为普通模块和基本模块,基本模块不依赖其他任何模块,提供最基础的支持,比如基本类、lang包等等。
整个系统的结构就可以简易的用下图表示:

image-cd7f26ae44084ab9b332d6d068f922ef_1639726193582

那么如何编写一个模块呢?其实很简单。模块的定义主要是对模块下module-info.java文件的一个定义,这个文件里包含了该模块的基本信息,包括模块名称,依赖,导出内容等等,并且这个文件的存在标明了整个系统的模块化标志。注意的是模块的声明(属性)中不包含版本号(version),模块化的不在解决版本选择的问题,这个问题应该交给构建工具和容器来解决。至于代码具体的调用就和普通jar包引用一样,import了之后即可。

module-info.java文件通常位于源码的根目录下。

module me.minei.hello {
}

这样就声明了一个模块,模块命名和包命名类似。

requires关键字表明了模块的依赖和引用,所有的模块默认引用和依赖java.base模块,就像旧版本里所有包都可以使用lang包而不用import。
exports关键字表明你要向外提供服务的包,如果不exports,那么默认模块不可以被任何模块所依赖和引用。通常模块名称就是你要导出的名称。不可以导出空的包,也就是说导出的包下要有文件。同时也可以指定导出包到某一个模块,例如:

module me.minei.hello {
    requires java.base;
    exports me.minei.hello to ...;
}

模块之间的隔离性:
Java 9模块之间有着非常强的隔离性。requires关键字就像import,但是比import更强,他需要被依赖的模块导出包(exports)或者导出包到引用的模块,也就是说模块之间的依赖需要提供者和使用者双方达成一致(导出和依赖)方能使用,任何非法的引用都会导致编译和运行时异常。

exports的导出是唯一的,就是说导出这个包仅仅是这个包下面的文件外部能够访问,但是对子包,或者父包的访问都是不被允许的。跨模块的依赖和引用也是不允许的,比如a依赖b,b依赖c但是a不能够访问c。

那么这样就出现了一个问题,存在这么一种情况,a依赖b,但是b依赖c,a不能访问c,但是我又需要能够访问到c,那怎么办?a可以选择依赖c,但是c没有导出到a呢?这时就需要transitive关键字了,就像这样:

module me.minei.hello {
    requires transitive java.base;
    exports me.minei.hello;
}

那么a就能访问c了。这样的设计其实也是加强了模块之间的隔离性,降低了耦合性。

Module System的这些设计使得代码具有非常强的隔离性和安全性,避免非法的引用和错误。但同时,这种隔离性都是针对于模块之间的,如果你将模块当作普通jar包来依赖的话,隔离性就自然不存在了,这样对就版本的Java做了兼容同时对迁移到模块化结构也是有好处的。

下面介绍两种比较特殊的模块类型。

The unnamed module 未命名模块,其实就是普通的jar包(没有module-info),这种情况模块系统是会默认把他们当作未命名的模块。这种模块可以访问所有其他普通模块,但是反过来不行,这样避免了模块系统的混乱,同时存在模块化的包和非模块化的包。

如果一个包同时出现在未命名和普通模块中会被当作普通的模块来处理,旧版本编译的jar会被当作未命名的模块处理。

Automatic module 自动模块,这种模块是指普通模块中未导出部分的包会被系统自动处理成自动模块,可以引用其他所有普通模块(前提是导出了),包括同类型模块。自动模块提供了一个更加中立的环境来处理模块和非模块(普通jar包)这种混合环境,并且这种模块是不可控的。

上面两种模块的出现其实都是为了更好的解决从旧环境迁移到模块化环境中的问题,比如不能模块化或者完全模块化,做到了向下的兼容。

可供参考的一些文档:
http://openjdk.java.net/projects/jigsaw/spec/sotms/
https://docs.oracle.com/javase/9/index.html