JDK工程结构的问题
在说Java模块化系统之前,先来说说Java9之前的JDK在工程结构上的问题,从JDK本身的问题说起,Java从1996年发布第一版到2017年发布Java9,中间经历了近20年的时间,在这期间发布了无数个大大小小的版本用来支撑新的特性。
在Java新特性越来越丰富的同时,也带来了相应的问题。JDK8是一个广泛使用的版本,我们就以JDK8为例,先来看看目录结构
在JDK8以及之前的版本,我们安装的时候会安装两部分内容
- JDK(Java Development Kit)主要用于开发者提供了开发工具和环境
- JRE(Java Runtime Environment)主要提供Java运行时环境
JDK是开发Java应用的完整套装,而JRE则是为了支持已编译的Java程序在任何计算机上运行而设计的环境。JDK内部包含了JRE,以方便开发者在开发的同时也能直接运行和测试他们的代码
在jre/bin目录下有一个rt.jar
文件,表示的是runtime,即运行时。JVM会加载这整个文件来支持Java运行时环境,而rt.jar
文件的大小已经达到50-60M,也就是说在运行你的Java程序之前,就需要花这么多内存来加载Java运行时环境,我们来看看rt.jar
中包含哪些内容
我们使用解压工具打开rt.jar
可以清晰的看到,里面包括的内容很全,基本上包括了Java的方方面面,都给你加载进去了。就算你只写一个HelloWord,它也给你加载了Applet、awt等你根本不会用到的东西。现在的服务器和个人电脑随便都是8G、16G以上的内存,对于rt.jar
占用的这点内存可能没什么感觉,但在一些对于内存很敏感的领域,Java这种方式就显得不太合理。Java官方可能也意识到了这个问题,所以在Java9的时候推出了Java平台模块系统(Java Platform Module System,JPMS)
Java平台模块系统(Java Platform Module System,JPMS)
JPMS是在Java9发布的,其实从Java7开始官方就在准备JPMS,本来准备在Java8中引入JPMS的,实在是改动太大,到Java9中才正式发布。其实做过开发的都会想的到,模块化相当于要从整体上重构整个系统,结构调整巨大,对于整个系统考验是很大的,这可能也是JPMS从Java7开始准备,直到Java9才发布的原因。
前面我们说过,在Java8及之前的版本中,其实是包括JDK和JRE两个部分的。但从Java9之后,就没有专门的JRE了,因为模块化之后,自己可以选择JVM加载哪些模块,相当于按需加载就行了,你写HelloWord可能只需要一个加载java.base
就行了。 下面是JDK11的结构
在JDK安装目录下有一个jmods目录,在该目录下就定义了JDK中各个模块,以.jmod
结尾的文件就是JDK中定义的各个模块,我们打开java.base.jmod文件看一下
可以看到在java.base
中class/java/
目录中,包括了一些Java基础用到的lang、math、nio
等基础包,没有包插applet、awt
等不常用的包。 这样按模块化划分之后,一方面可以更好的管理JDK的各个模块,另外一方面对于使用者来说,也不用像旧版JDK那样一股脑把所有的内容加载到JVM中,这样更加合理,也更节省资源。
Java模块化系统以克服当前所存在的局限时,主要设定了两个目标
- 对JDK本身进行模块化
- 提供一个应用程序可以使用的模块系统
上面我们已经看到了JDK本身模块化的改造,下面我们就来看看应用程序是如何使用模块系统的
应用程序模块化实现
应用程序实现模块化只需要简单的三步就可以完成
- 定义模块
- 导出模块
- 引用模块
先整体看一下,我们定义了三个模块userModule、OrderModule、GoodsModule,项目的结构如下:
module-info.java文件
module-info.java
文件是用来定义模块的,在模块的src目录下定义一个module-info.java
文件,内容如下
java
module UserModule {
}
简单的定义了一个UserModule的模块,里面内容是空的 现在三个模块是独立的,假如在OrderModule中需要使用UserModuler的UserService,直接使用肯定是引用不到的,需要进行exports
exports
如果需要将模块中的内容对外,需要在module-info.java
中把相应的包exports出去
java
module UserModule {
exports com.user;
}
如上所示,表示UserModule将com.user
包对外提供服务
requires
如果需要引用其他模块的内容,需要先使用requires将模块引用进当前模块,如下
java
module OrderModule {
requires UserModule;
}
如上所示,表示OrderModule引用了UserModule,如此在OrderModule中就可以使用UserModule中exports出来的包
java
public static void main(String[] args) {
UserService userService = new UserService();
userService.register();
}
如上所示,在OrderModule中就可以直接引入UserService了
如果你只想将UserModule开放给GoodsModule,可以这样写
java
module UserModule {
exports com.user to GoodsModule;
}
这样就算OrderModule引用了UserModel,也没办法使用UserService,因为UserModule只开放给GoodsModule了
以上就是一个使用模块化来实现模块之间相关调用的简单例子,当然实际的模块化系统不可能这么简单,此处只起到一个抛转引玉的作用,如果对模块化系统比较感兴趣可以去JDK官网了解详细的信息
模块化的好处
1、强封装性
类和包可以被模块化,只有模块之间声明的接口是对外可见的,提高了代码的封装性,减少了不必要的耦合
2、明确的依赖管理
块之间通过requires声明依赖,明确指出了哪些模块需要哪些其他模块,避免了隐式依赖和类路径冲突
3、运行时性
JVM可以仅加载运行应用程序所需的模块,减少了内存占用,提高了启动速度和运行时性能
4、安全性和隔离
模块边界强化了安全性,限制了代码的访问权限,降低了攻击面
通过模块化,Java开发者可以构建更加健壮、高效且易于维护的大型应用程序
你的项目有用到模块化吗?欢迎给我留言讨论!