1.模块化概述
Jar Hell
- jar文件无法控制别人访问其内部的public的类
- 无法控制不同jar包中,相同的类名(包名+类名)
- Java运行时,无法判定classpath路径上的jar中有多少个不同版本的文件。Java加载第一个符合名字的类
- Java运行时,无法预判classpath路径上是否缺失了一些关键类
模块化系统
- 模块化必须遵循的三个原则
- 强封装性:一个模块必须能够对其他模块隐藏其部分代码
- 定义良好的接口:模块必须向其他模块公开定义良好且稳定的接口
- 显式依赖:明确一个模块需要哪些模块的支持才能完成工作
Jigsaw 拼图
- Java 9开始引入新的模块化系统:Jigsaw 拼图
- 以模块(module)为中心
- 对JDK 本身进行模块化
- 提供一个应用程序可以使用的模块系统
- 优点
- 可靠的配置
- 强封装性
- 可扩展开发
- 安全性
- 性能优化
2.模块创建和运行
Java Jigsaw
- 自Java 9 推出,以模块为中心
- 在模块中,仍以包-类文件结构存在
- 每个模块中,都有一个module-info.java
- 说明这个模块依赖哪些其他的模块,输出本模块中哪些内容
1 | module java.prefs { |
命令行创建
- 新建一个项目主目录,如F:\temp\JavaModuleTest
- 创建src目录(放java),modules目录(放class),lib目录(放jar)
- 在src下面建立一个目录module.hello(模块名字,可自由定)
- 在module.hello目录下,建立cn\hello目录,再建立HelloWorld.java
- 在module.hello目录下,建立一个module-info.java
- 编译/运行/打包
- 链接jlink,制作自定义运行时映像(custom runtime image)
- 舍弃无用庞大的JDK库
- 适合在容器中快速部署运行
Eclipse创建
- 新建一个Java Project项目主目录
- 可以在创建的时候,直接选择创建module-info.java
- 或者选中项目,右键configure选择Create module-info.java
- 创建cn.hello.HelloWorld.java
- 可以选择在默认src目录下
- 可以新建一个source folder,取名module.hello,再创建包结构和类文件
- 修改module-info.java
- 编译/运行/打包
注意事项
- Java Module和Maven Multi Module 不一样
- 使用JDK 9+,不强制创建Java Module,即不创建module-info.java
- Module的命名是由module-info.java来控制
- requires
- exports
3.模块信息文件
module-info.java
- 模块安全控制的核心
- 是模块和外界沟通的总负责
- 名字和内容组成
- 模块名字
- 模块名称必须唯一
- 可以不和包名相同
- 使用有代表性的词语
- 不需要包括版本号
requires调用其他的模块
- java --list-modules查看系统提供的模块
- java --describe-module看某一个模块
- requires可以添加多个
- 单纯requires,模块依赖不会传递
- requires N, 编译和运行都依赖于N
- requires transitive N,编译和运行都传递依赖于N
- requires static N,编译依赖于N,运行可选
- requires transitive static N,编译传递依赖于N,运行可选
exports输出当前模块
- 只有输出,别人才能使用
- exports可以指定某些包输出
- exports <package>
- 限定输出到特定的模块使用
- exports <package> to <module1>, <module2>;
opens将当前模块开放用于反射
- exports导出的包的public部分可以反射,其他权限修饰的内容和未导出的内容无法反射(setAccessible(true)也无效)
- opens可以打开一些包,其他模块可以反射调用这些包及内容
- open module 打开整个模块
- 打开一个包
- opens <package>
- 仅对某些模块打开一个包
- opens <package> to <module1>, <module2>;
4.服务
- Java 模块系统引入的新功能,实现解耦
- 模块对外只暴露接口,隐藏实现类
- provides提供接口,with实现类(不导出)
- uses消费接口
- ServiceLoader加载接口的实现类
1 | module module.first { |
ServiceLoader通过load加载接口的实现类(with语句提供)
每次load,(默认情况下)都会产生新的各自独享的实例,没有唯一的服务实例
可以调用reload进行刷新
Java 模块系统提供两种方法创建服务实例
- 服务实现类有public的无参构造函数
- 使用单独的静态提供者方法
- 一个名为provider的public static 无参数方法
- 返回服务接口或子类
1 | provides first.p1.Shoe with first.p1.ShoeFactory; |
5.Java模块化应用
Java 模块化系统(Java 9+)
- 从根源上对JDK进行模块化,降低最终程序运行时负载
- 在jar层上增加一个module机制
- 引入exports/requires/opens明确模块边界和依赖关系,程序更隐私安全
- 引入服务provides/uses使得程序更解耦
- jlink制作运行时映像,使运维更高效
向Java 模块化系统迁移的制约因素
- Java模块化系统尚未成熟,存在较多变化
- 只有11是LTS,9/10/12/13均是STS
- Java 9+ JDK版权收费,很多程序员和厂商还是使用Java 8
- 已有的庞大的第三方库,基本不是基于模块开发的,完全融入或使用模块重新开发有困难
- 构建工具和开发工具IDE尚未大力支持
- Java模块化系统尚未成熟,存在较多变化
已有的模块系统OSGi
- OSGi, Open Service Gateway Initiative, https://www.osgi.org/
- 在OSGi容器里面运行bundle,通过类加载器来控制类的可见性
Java模块化 vs OSGi
- Java 更偏源头控制,从JDK开始分模块开发和部署
- OSGi更偏动态性控制,对bundle进行全生命周期控制
- Java模块化更年轻,尚未成熟,OSGi已经发展多年,更完整
- 两者互相借鉴和互操作
- https://www.infoq.com/articles/java9-osgi-future-modularity/