0. 模块是什么
Java Platform Module System(Java平台模块系统).是在Java 9 中引入的一个模块化组件.
0.1 逻辑关系
模块系统 最直观的例子就是官方API文档结构发生的变化(所有内容以java11为例):
![](https://img.haomeiwen.com/i6965192/fdbb2616e0f163ef.png)
首先看到的就是module列表, 而不是原来的package列表. 点击进入java.base
这个module, 看到的才是熟悉的package列表. 甚至在这些页面中,还会带有模块之间的依赖关系图.
![](https://img.haomeiwen.com/i6965192/651d9f60125e9565.png)
![](https://img.haomeiwen.com/i6965192/cb2de84332dab261.png)
逻辑上讲, 包是用来组织类和接口的, 而模块就是用来组织包的, 模块在包的外层又添加了一层封装.
0.2 物理结构
物理结构上, 类和接口是文件, 包是层级目录, 指定包名的文件放入相应的目录. 而模块则是在包的外层又添加了一层目录,作为模块的根目录, 并在根目录下放置模块文件module-info.java
, 以java.sql为例:
![](https://img.haomeiwen.com/i6965192/35d16805f8e191ee.png)
可以看到java.sql这个模块的底下有两个package:
java.sql
和javax.sql
, 都是层级目录结构. 而模块根目录java.sql的命名规范虽跟package相同,但是并不是层级目录而只是一个普通的目录.java.sql模块根目录下的
module-info.java
内容:
module java.sql {
requires transitive java.logging;
requires transitive java.transaction.xa;
requires transitive java.xml;
exports java.sql;
exports javax.sql;
uses java.sql.Driver;
}
-
requires
表示该模块依赖的其他模块 -
exports
表示该模块对外公开的包, 这也是模块的重要作用之一, 控制其他模块对该模块的包的访问.
1. 自己写一个无用模块
既然知道了他的逻辑关系和物理结构, 就可以模仿它去实现自己的module了.
1.1 创建项目目录
![](https://img.haomeiwen.com/i6965192/cc1b9af93179e2a3.png)
- MyModules: 项目目录
- src: 源码目录
- src/com.my.firstmodule: 模块根目录
- mods: 存放编译的class文件
- target: 存放打包的jar文件
- module-info.java: 模块文件
1.2 编写代码
//MyFirstModule.java
package com.my.firstmodule;
public class MyFirstModule {
public static void main(String[] args) {
System.out.println("my first module!");
}
}
//module-info.java
module com.my.firstmodule {
requires java.base;
}
module-info.java
只是声明了所依赖的java.base模块. 事实上可以省略对 java.base的依赖声明.
1.3 编译模块
在项目根目录下执行javac
javac -d mods/com.my.firstmodule src/com.my.firstmodule/module-info.java src/com.my.firstmodule/com/my/firstmodule/MyFirstModule.java
-d 选项指定编译的类文件输入目录, 后面跟着需要编译的源文件.编译后的目录结构
MyModules/
├── mods
│ └── com.my.firstmodule
│ ├── com
│ │ └── my
│ │ └── firstmodule
│ │ └── MyFirstModule.class
│ └── module-info.class
├── src
│ └── com.my.firstmodule
│ ├── com
│ │ └── my
│ │ └── firstmodule
│ │ └── MyFirstModule.java
│ └── module-info.java
└── target
1.4 运行模块
java --module-path mods --module com.my.firstmodule/com.my.firstmodule.MyFirstModule
运行结果: my first module!
1.5 打包模块
jar -c -f target/myfirstmodule-1.0.jar -e com.my.firstmodule.MyFirstModule -C mods/com.my.firstmodule .
在target目录下生成了jar文件
─ target
└── myfirstmodule-1.0.jar
运行jar文件
java --module-path target/myfirstmodule-1.0.jar --module com.my.firstmodule/com.my.firstmodule.MyFirstModule
运行结果: my first module!
我们的第一个简单的模块已经完成并运行成功了! 可喜可贺!可喜可贺! 但是感觉上命令行中写的更复杂了, 那为什么有了package之后还要引入module呢?
3. 为什么要引入模块系统
在传统的面向对象设计中, 模块化的机制在Java语言中是由package提供的. 那为什么要再添加Module概念, 让事情更加复杂呢?
这方面的描述在 JSR-376中, 有以下几点:
-
可靠配置(Reliable configuration):
classpath不能表示组件之间的关系, 所以如果有依赖的组件缺失, 只能在尝试使用它的时候才能发现. classpath允许从不同组件加载相同包的类, 导致无法预测的行为和难以侦测的错误.
模块系统可以声明组件之间的依赖关系 -
强封装(Strong encapsulation)
Java语言和虚拟机没有提供一种机制, 让组件禁止其他组件访问自己的内部package. 所以只要是public的package, 外部组件是可以随意访问的.
模块系统则可以声明哪些包可以被访问, 那些包不能被访问.
java没有提供组织其他组件访问某个组件的内部包(package)
jPM允许某个组件可以声明哪些包可以被访问.哪些包不可以. -
可扩展的平台(A scalable platform)
JVM和JRE在尺寸上不断增大, 使得很多小设备很难运行JVM或者java应用.
JavaSE本身使用模块系统分解后, 应用可以按需定制, 通过配置, 只包含自己需要的功能模块, 从而缩小应用尺寸.
这一点可能是java在物联网时代抓住发展机遇的必备条件. -
更好的平台完整性(Greater platform integrity)
随意的访问平台内部的API, 既不安全也会增加维护成本. 模块系统提供的强封装可以保证内JavaSE平台内部API不被访问. -
更高的性能(Improved performance)
许多事先的, 整体程序优化技术, 如果能确定类引用的是某几个特定组件中的类, 而非运行时加载的任意类, 会更有效果. 程序性能尤其在应用组件同平台组件联合优化时增强.
现在还没听说哪个大型软件组织使用模块系统. 虽然大多数语言都有module这个概念, 但引入java中到底能不能带来非常大的好的转变还需要时间验证.理论总是需要实践检验的.
4. 文章推荐
Java 9 Modules(Part 1)
Java 9 Modules(Part 2)
Java 9 Modules(Part 3)
JSR-376
网友评论