什么是模块系统
JAVA 9 最大的变化之一是引入了模块系统(Jigsaw 项目)。
模块就是代码和数据的封装体。模块的代码被组织成多个包,每个包中包含Java类和接口;模块的数据则包括资源文件和其他静态信息。
Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。这个文件由根目录中的源代码文件 module-info.java 编译而来。该模块声明文件可以描述模块的不同特征。
为什么要使用模块化
java开发者都知道,使用java开发应用程序都会遇到一个问题,Jar hell,他就像windows里的dll hell。
比如我们启动一个不算大的应用,但依赖了很多的jar,如下图:
摘自:Mark Reinhold的演讲 www.youtube.com/watch?v=l1s…
这是很常见的。虽然你可以使用 “java -Djava.ext.dirs=lib xxx” 让命令行小一些,但不可否认,他的classpath就是那么长。顺便说一句,java9中不允许使用extdirs了。
另一方面, jdk 本身有很多的 api :
如何使用模块系统
在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,如下所示。下面给出了一个模块com.mycompany.mymodule的最基本的模块声明。
module com.runoob.mymodule { }
创建模块
接下来我们创建一个 com.runoob.greetings 的模块。
第一步
创建文件夹 /home/xx/JAVA/src,然后在该目录下再创建与模块名相同的文件夹 com.runoob.greetings。
第二步
在 /home/xx/JAVA/src/com.runoob.greetings 目录下创建 module-info.java 文件,代码如下:
module com.runoob.greetings { }
module-info.java 用于创建模块。这一步我们创建了 com.runoob.greetings 模块。
第三步
在模块中添加源代码文件,在目录 /home/xx/JAVA/src/com.runoob.greetingscomrunoobgreetings 中创建文件 Java9Tester.java,代码如下:
package com.runoob.greetings; public class Java9Tester { public static void main(String[] args) { System.out.println("Hello World!"); } }
第四步
创建文件夹 /home/xx/JAVA/src/,然后在该目录下创建 com.runoob.greetings 文件夹,编译模块到这个目录下:
javac -d mods/com.runoob.greetings src/com.runoob.greetings/module-info.java src/com.runoob.greetings/com/runoob/greetings/Java9Tester.java
第五步
执行模块,查看输出结果:
C:/>JAVA> java --module-path mods -m com.runoob.greetings/com.runoob.greetings.Java9Tester Hello World!
module-path 指定了模块所在的路径。
-m 指定主要模块。
內建的module
jdk原生的包被归并到內建的module里,如java.base模块:
module java.base{ exports java.io; exports java. lang ; exports java.lang. annotation ; exports java.lang.invoke; exports java.lang.module; exports java.lang.ref; exports java.lang.reflect; exports java.lang.math; exports java.lang.net; //... }
所有的应用都会默认依赖 java.base,就像以前我们不用显式的 “import java.lang.*;” 一样。
这里验证了前面 HelloWorld 中,为什么反编译模块文件之后会多了一个:”requires java.base;”。
下面的 com.foo.app 模块,不需要显式地引入java.base:
如果此时com.foo.bar 增加了 com.foo.baz 模块的引用。
那么,我们知道 com.foo.bar 也隐式 引入了 java.base。
同样的道理,com.foo.baz 模块也隐式引用了 java.base:
可靠的配置
继续深入下去,我们知道 java.sql 引用了其他大量的api,那么下图就不难理解了。
目前的模块结构,称为可读的模块,提供了可靠的配置。
如果引用了不存在的module,和jar一样,你同样会触发 xx not found.
编译时:
运行时:
如果引用的模块没有导出某个类,那么是不可访问的,这称为强封装。
比如 com.foo.bar 模块中有一个内部类BetaImpl:
比如 com.foo.bar 模块中有一个内部类BetaImpl:
那么在 com.foo.bar 模块的主动引用模块 com.foo.app 中如下使用 BeatImpl:
在编译时,会触发异常:
就是说:BetaImpl不可访问,因为包 com.foo.bar.beta.internal 包没有被导出。
同样,即便使用导出版本编辑成功,而运行时引用了未导出版本模块:
查看內建的模块
$ jmod list $JAVA_HOME/jmods/java.base.jmod classes/module-info.class classes/apple/security/AppleProvider$1.class ... classes/java/lang/Object.class ... bin/java bin/keytool ... conf/security/java.policy ...
查看更多内建模块:
$ java --list-modules java.activation@9 java.base@9 java.compiler@9 java.corba@9 java.datatransfer@9 java.desktop@9 //...节省篇幅略