Java模块化

1. Java模块化概述

1.1 JDK8及以前开发模式

  • 每个java文件被明确地放入到一个包中
  • java文件编译后的class文件,可以压缩为jar包,供别的程序调用
  • 一个程序可以使用类库,类库通常以jar包呈现
  • 将所有程序jar包,类库jar包,都放在classpath上,来运行程序
  • jar为中心

1.2 Jar Hell

  • jar文件无法控制被人访问其内部的public的类
  • 无法控制不同jar包中,相同的类名(包名+类名)
  • java运行时,无法判定classpath路径上的jar有多少个不同版本的文件,java加载第一个符合名字的类
  • java运行时,无法预判classpath路径上是否缺失了一些关键类

1.3 模块化原则

  • 强封装性:一个模块必须能够对其他模块隐藏其部分代码
  • 定义良好的接口:模块必须向其他模块公开定义良好且稳定的接口
  • 显示依赖:明确一个模块需要哪些模块的支持才能完成工作

1.4 Java模块化系统

  • Java9开始引入新的模块化系统:Jigsaw拼图

    • 模块(module)为中心
    • 对JDK本身进行模块化
    • 提供一个应用程序可以使用的模块系统
  • 以Java11为例

    • 一共71个模块
    • 最底层的是java.base
    • 每个模块都有明确的依赖模块,不存在循环依赖
    • 使用java --list-modules,可以查看JDK的模块列表
    • 每个类都自动引用java.base模块
    • 使用java --describe-module查看平台模块声明

2. 模块创建和运行

2.1 Java Jigsaw

  • 自Java9推出,以模块为中心

  • 在模块中,仍以包-类文件结构存在

  • 在每个模块中,都有一个module-info.java

    • 说明这个模块依赖哪些其他的模块,输出本模块哪些内容

      module java.prefs {
          requires java.xml;
          export java.util.prefs;
      }
      
  • Module的命名时由module-info.java来控制

  • 链接jlink,制作自定义运行时镜像(custom runtime image)

    • 舍弃无用庞大的JDK库
    • 适合在容器中快速部署运行

3. 模块信息文件

3.1 module-info.java

  • 模块安全控制的核心

  • 模块和外界沟通的总负责

  • 名字和内容组成

  • 模块名字

    • 模块名称必须唯一
    • 可以不和包名相同
    • 使用有代表性的词语
    • 不需要包括版本号
  • 示例

    module module.hello {
        //输出给别人使用
        exports cn.hello;
        //本模块需依赖的模块
        requires java.xml;
    }
    

3.2 requires调用其他模块

  • requires可以添加多个
  • 单纯requires,模块依赖不会传递,即调用module.hello不会自动调用java.xml
  • 传递依赖使用require transitive
  • requires N,编译和运行都依赖于N
  • requires transitive N,编译和运行都传递依赖于N
  • requires static N,编译依赖于N,运行可选
  • requires transitive static N,编译传递依赖于N,运行可选

3.3 exports将当前模块输出

  • 只有输出,别人才能使用
  • exports可以指定某些包输出,exports
  • 限定输出到特定的模块使用,exports to ,

3.4 opens将当前模块开放用于发射

  • exports导出的包的public部分可以反射,其他权限修饰的内容和未导出的内容无法反射(setAccessible(true)也无效)

  • opens可以打开一些包,其他模块可以反射调用这些包及内容

  • open module打开整个模块

  • 打开一个包,opens

  • 仅对某些模块打开一个包,opens to ,

    open module module.first {
        exports first.p1;
        // opens firts.p1;
    }
    

4. 服务

4.1 服务介绍

  • Java模块系统引入的新功能,实现解耦

  • 模块对外只暴露接口,隐藏实现类

  • provides提供接口,with实现类(不导出)

  • uses消费接口

  • ServiceLoader加载接口的实现类

  • 示例

    module module.first {
        exprots first.p1;
        provides first.p1.Shoe with p2.DoubleStar;
    }
    
    module module.second {
        requires module.first;
        uses first.p1.Shoe;
    }
    
    SeviceLoader<Shoe> objs = ServiceLoader.load(Shoe.class);
    for (Shoe obj : objs) {
        obj.walk();
    }
    
  • ServiceLoader通过load加载接口的实现类(with语句提供)

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

  • 可以调用reload进行刷新

4.2 创建服务实例

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

  • 服务实现类有public的无参构造函数

  • 使用单独的静态提供者方法

    • 一个名为provider的public static无参数方法
    • 返回服务接口或者子类
    provides first.p1.Shoe with first.p1.ShoeFactory;
    
    public class ShoeFactory {
        public static Shoe provider() {
            Shoe result = new DoubleStar();
            return result;
        }
    }
    

5. Java模块化应用

5.1 Java模块化特点

  • 从根源上对JDK进行模块化,降低最终程序运行时负载
  • 在jar层上增加一个module机制
  • 引入exports/requires/opens明确模块边界和依赖关系,程序更隐私安全
  • 引入服务provides/uses使得程序更解耦
  • jlink制作运行时映像,使运维更高效
posted @ 2022-08-31 23:30  hunter-w  阅读(780)  评论(0编辑  收藏  举报