掘金 后端 ( ) • 2024-05-04 10:25

JDK工程结构的问题

在说Java模块化系统之前,先来说说Java9之前的JDK在工程结构上的问题,从JDK本身的问题说起,Java从1996年发布第一版到2017年发布Java9,中间经历了近20年的时间,在这期间发布了无数个大大小小的版本用来支撑新的特性。

在Java新特性越来越丰富的同时,也带来了相应的问题。JDK8是一个广泛使用的版本,我们就以JDK8为例,先来看看目录结构

e7d906c53ea64aff967ef1fddf1b3ca1.png

在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中包含哪些内容

c6d98b9b55644c68bf52cb0f70d2f675.png 我们使用解压工具打开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的结构

7940167be7f54191a8e5143b5f17e95e.png 在JDK安装目录下有一个jmods目录,在该目录下就定义了JDK中各个模块,以.jmod结尾的文件就是JDK中定义的各个模块,我们打开java.base.jmod文件看一下

22121f4fdb974a629f81b7750e5b9023.png 可以看到在java.baseclass/java/目录中,包括了一些Java基础用到的lang、math、nio等基础包,没有包插applet、awt等不常用的包。 这样按模块化划分之后,一方面可以更好的管理JDK的各个模块,另外一方面对于使用者来说,也不用像旧版JDK那样一股脑把所有的内容加载到JVM中,这样更加合理,也更节省资源。

Java模块化系统以克服当前所存在的局限时,主要设定了两个目标

  • 对JDK本身进行模块化
  • 提供一个应用程序可以使用的模块系统

上面我们已经看到了JDK本身模块化的改造,下面我们就来看看应用程序是如何使用模块系统的

应用程序模块化实现

应用程序实现模块化只需要简单的三步就可以完成

  • 定义模块
  • 导出模块
  • 引用模块

先整体看一下,我们定义了三个模块userModule、OrderModule、GoodsModule,项目的结构如下:

25eb70f2d8604c3291778ab80a04362b.png

module-info.java文件

module-info.java文件是用来定义模块的,在模块的src目录下定义一个module-info.java文件,内容如下

module UserModule {
    
}

简单的定义了一个UserModule的模块,里面内容是空的 现在三个模块是独立的,假如在OrderModule中需要使用UserModuler的UserService,直接使用肯定是引用不到的,需要进行exports

exports

如果需要将模块中的内容对外,需要在module-info.java中把相应的包exports出去

module UserModule {
    exports com.user;
}

如上所示,表示UserModule将com.user包对外提供服务

requires

如果需要引用其他模块的内容,需要先使用requires将模块引用进当前模块,如下

module OrderModule {
    requires UserModule;
}

如上所示,表示OrderModule引用了UserModule,如此在OrderModule中就可以使用UserModule中exports出来的包

    public static void main(String[] args) {
        UserService userService = new UserService();
        userService.register();
    }

如上所示,在OrderModule中就可以直接引入UserService了

如果你只想将UserModule开放给GoodsModule,可以这样写

module UserModule {
    exports com.user to GoodsModule;
}

这样就算OrderModule引用了UserModel,也没办法使用UserService,因为UserModule只开放给GoodsModule了

以上就是一个使用模块化来实现模块之间相关调用的简单例子,当然实际的模块化系统不可能这么简单,此处只起到一个抛转引玉的作用,如果对模块化系统比较感兴趣可以去JDK官网了解详细的信息

模块化的好处

1、强封装性

类和包可以被模块化,只有模块之间声明的接口是对外可见的,提高了代码的封装性,减少了不必要的耦合

2、明确的依赖管理

块之间通过requires声明依赖,明确指出了哪些模块需要哪些其他模块,避免了隐式依赖和类路径冲突

3、运行时性

JVM可以仅加载运行应用程序所需的模块,减少了内存占用,提高了启动速度和运行时性能

4、安全性和隔离

模块边界强化了安全性,限制了代码的访问权限,降低了攻击面

通过模块化,Java开发者可以构建更加健壮、高效且易于维护的大型应用程序

你的项目有用到模块化吗?欢迎给我留言讨论!