引言
随着Java技术的发展,开发人员面临的挑战之一是如何有效地管理和组织大型项目的依赖关系。传统的类路径(classpath)方法虽然简单,但在大型项目中却难以管理,尤其是在面对复杂的依赖关系时。为了解决这些问题,Java 9引入了Java Platform Module System(JPMS),也称为Jigsaw项目。本文将探讨JPMS产生的原因、解决的问题以及其实现原理,并通过示例演示如何使用JPMS。
JPMS产生的原因
在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)
解决的问题
JPMS解决了上述提到的问题,其实核心思路就是按需加载,具体来说:
- 清晰的依赖管理:通过明确的模块依赖声明,开发人员可以轻松地管理项目的依赖关系。
- 增强的封装:模块之间默认不可见,只有通过明确的导出声明才能访问其他模块的公共API。
- 提高安全性:限制对内部API的访问,增强了系统的安全性。
- 更好的性能:模块化应用程序可以更快地启动,因为Java运行时只需要加载所需的模块。
JPMS原理介绍
模块
模块是JPMS的基本单位。每个模块都有自己的命名空间,并且可以通过模块描述文件(module-info.java
)来声明模块的依赖关系和其他元数据。
模块描述文件
每个模块都有一个特殊的源文件module-info.java
,其中包含了模块的元数据,如模块名称、导出的包、依赖的其他模块等。
模块路径
在Java 9及之后的版本中,引入了模块路径(module path),用于指定模块的位置。模块路径可以包含多个模块,每个模块都可以包含多个包。
模块之间的可见性
模块之间默认是不可见的,这意味着一个模块不能直接访问另一个模块的内部细节。为了使一个模块的包对其他模块可见,需要使用exports
关键字来导出这些包。同样,使用requires
关键字来声明一个模块依赖于其他模块。
自动模块
在Java 9中,位于类路径上的非模块化代码被称为“自动模块”。自动模块默认是可见的,但只能访问其他模块公开的部分。
模块层次结构
模块之间可以形成层次结构,通过依赖关系连接起来。根模块(通常为java.base
)是所有其他模块的基础,它包含了Java核心类库。
示例
假设我们有两个模块:com.example.mylibrary
和 com.example.app
。
模块 com.example.mylibrary
module-info.java
:module com.example.mylibrary { requires java.base; exports com.example.mylibrary.api; }
模块 com.example.app
module-info.java
:module com.example.app { requires com.example.mylibrary; requires java.base; exports com.example.app; }
示例代码
com.example.mylibrary
中的代码
com/example/mylibrary/api/MyLibrary.java
:package com.example.mylibrary.api; public class MyLibrary { public static String helloWorld() { return "Hello, World!"; } }
com.example.app
中的代码
com/example/app/App.java
:package com.example.app; import com.example.mylibrary.api.MyLibrary; public class App { public static void main(String[] args) { System.out.println(MyLibrary.helloWorld()); } }
编译和运行
-
编译:
javac --module-source-path src --class-path . -m com.example.mylibrary:src/com/example/mylibrary javac --module-source-path src --class-path . -m com.example.app:src/com/example/app
-
运行:
java --module-path . -m com.example.app/com.example.app.App
结论
JPMS通过引入模块的概念,为Java应用程序提供了一种更强大的方式来管理依赖关系和控制包的可见性。这不仅提高了代码的可维护性和安全性,还使得构建大型应用程序变得更加容易。随着Java平台的不断发展,模块化将成为Java开发的标准做法。