少女祈祷中...

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
2
3
4
5
module java.prefs {
requires java.xml;

exports java.util.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
2
3
4
5
6
7
8
9
10
11
12
13
14
module module.first {
exprots first.p1;
provides first.p1.Shoe with first.p2.DoubleStar;
}

module module.second {
requires module.first;
uses first.p1.Shoe;
}

ServiceLoader<Shoe> objs = ServerLoader.load(Shoe.class);
for(Shoe obj : objs) {
obj.walk();
}
  • ServiceLoader通过load加载接口的实现类(with语句提供)

  • 每次load,(默认情况下)都会产生新的各自独享的实例,没有唯一的服务实例

  • 可以调用reload进行刷新

  • Java 模块系统提供两种方法创建服务实例

    • 服务实现类有public的无参构造函数
    • 使用单独的静态提供者方法
      • 一个名为provider的public static 无参数方法
      • 返回服务接口或子类
1
2
3
4
5
6
7
8
provides first.p1.Shoe with first.p1.ShoeFactory;

public class ShoeFactory {
public static Shoe provider() {
Shoe result = new Shoe();
return result;
}
}

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尚未大力支持
  • 已有的模块系统OSGi

    • OSGi, Open Service Gateway Initiative, https://www.osgi.org/
    • 在OSGi容器里面运行bundle,通过类加载器来控制类的可见性
  • Java模块化 vs OSGi