JDK9 - Module

JDK9引入了模块系统,这篇笔记简单介绍了引入module机制的初衷,module的定义、使用与打包。

Overview

没有引入module之前的JDK8项目结构如下,一个项目下的代码按照package进行组织。
在这里插入图片描述

引入了module之后的项目结构如下,在package之上还有一个module层次。

在这里插入图片描述

为什么引入module?

参考JSR376 以及这篇文章 有如下理由:

  1. Reliable configuration,在module之前,JVM通过classpath查找各个类,如果某个类缺失,只有当使用到这个类的时候才会发现,使用module可以清晰地配置各个module之间的依赖关系。
  2. Strong encapsulation,使用module机制可以定义一个module中那些包是对module外可见的,这就又添加了一层访问控制,增加了封装程度。
  3. Scalable Java platform,使用module之后,原先包含大量代码的Java SE被分割成95个module,代码更加容易维护。而且你可以只选择需要的module,例如在一些嵌入式设备上,如果不需要GUI,完全可以不把GUI模块包含在Java runtime中。
  4. Greater platform integrity,有些内部API是供内部使用的,不是给应用开发人员使用的,但是这些API在module机制之前能够被访问到,由于这些内部API有部分是平台相关的,使用这些API不利于各个平台统一(platform integrity),而module添加了一层访问控制,使得一些内部的API真正意义上地被隐藏了起来。
  5. Improved performance,性能更好了,具体为什么更好请查看JSR376

实践

下面使用如下的项目结构说明对模块的各种操作,项目分为两个模块 utilstest 模块,我会依次介绍模块的定义、模块的调用、模块的运行。

.
├── test
│   ├── module-info.java
│   └── org
│       └── example
│           └── test
│               └── TestStringUtils.java
└── utils
    ├── module-info.java
    └── org
        └── example
            └── utils
                └── StringUtils.java

8 directories, 4 files

StringUtils.java 的内容:

package org.example.utils;

public class StringUtils {
    public String echo(String s) {
        return s;
    }
}

TestStringUtils.java 的内容:

package org.example.test;

import org.example.utils.StringUtils;

public class TestStringUtils{
    public static void main(String[] args) {
        StringUtils util = new StringUtils();
        System.out.println(util.echo("hello"));
    }
}

module的定义

和Python中的模块类似,Java也使用一个文件来标志模块。如果一个目录中有名为 module-info.java 的文件,那么这个目录就被当成一个模块,在这个目录中可以存放各个package,下面就是一个模块的结构:

utils
├── module-info.java
└── org
    └── example
        └── utils
            └── StringUtils.java

3 directories, 2 files

module-info.java 中的内容如下:

module utils {
    exports org.example.utils;	// 表示暴露这个module中的这个包,这样模块外可以使用这个包
}

需要注意的是 module-info.java 中的模块名和 module-info.java 所在的目录名要一致。

如果要编译这个模块,把 module-info.java 当成普通的Java进行编译即可:

$ javac -d mod/utils utils/module-info.java utils/org/example/utils/StringUtils.java

其中 -d 参数表示把输出到 mod/utils 这个目录。

好了,现在 utils 模块编译好了,下面来看一下如何使用这个模块。

module的使用

我们在 test 模块的 module-info.java 中写入如下内容:

module test {
    requires utils;	// 表示这个模块需要使用到utils模块
    exports org.example.test; // 暴露test这个模块中的包
}

然后像之前一样编译这个模块,不过由于 test 模块需要用到 utils 模块,我们要使用 --module-path 告诉编译器去哪里找模块:

$ javac -d mod/test test/module-info.java test/org/example/test/TestStringUtils.java --module-path mod/  

运行module

代码写完了,现在执行下面的命令来运行 test 模块中的 TestStringUtils 类:

$ java --module-path mod/ --add-modules utils,test org.example.test.TestStringUtils                    
hello

稍微解释一下参数, --module-path 和上文一样,用来告诉编译器去哪里找module, --add-modules 后面接一个 , 分隔的列表,表示要加载的module,最后写明main函数所在的全限定类名 org.example.test.TestStringUtils

module的打包

和package一样,module也可以打包成一个文件,module文件的扩展名是 .jmod 。如果你的JDK版本大于8,你可以在JDK的安装目录下找到一个 jmod 目录,里面存放了JDK的各个module:

> ls /usr/lib/jvm/java-15-openjdk/jmods
java.base.jmod          java.instrument.jmod      java.se.jmod              jdk.compiler.jmod            jdk.internal.le.jmod                      jdk.jdwp.agent.jmod        jdk.naming.rmi.jmod
java.compiler.jmod      java.logging.jmod         java.smartcardio.jmod     jdk.crypto.cryptoki.jmod     jdk.internal.opt.jmod                     jdk.jfr.jmod               jdk.net.jmod
java.datatransfer.jmod  java.management.jmod      java.sql.jmod             jdk.crypto.ec.jmod           jdk.internal.vm.ci.jmod                   jdk.jlink.jmod             jdk.nio.mapmode.jmod
java.desktop.jmod       java.management.rmi.jmod  java.sql.rowset.jmod      jdk.dynalink.jmod            jdk.internal.vm.compiler.jmod             jdk.jshell.jmod            jdk.sctp.jmod
javafx.base.jmod        java.naming.jmod          java.transaction.xa.jmod  jdk.editpad.jmod             jdk.internal.vm.compiler.management.jmod  jdk.jsobject.jmod          jdk.security.auth.jmod
javafx.controls.jmod    java.net.http.jmod        java.xml.crypto.jmod      jdk.hotspot.agent.jmod       jdk.jartool.jmod                          jdk.jstatd.jmod            jdk.security.jgss.jmod
javafx.fxml.jmod        java.prefs.jmod           java.xml.jmod             jdk.httpserver.jmod          jdk.javadoc.jmod                          jdk.localedata.jmod        jdk.unsupported.desktop.jmod
javafx.graphics.jmod    java.rmi.jmod             jdk.accessibility.jmod    jdk.incubator.foreign.jmod   jdk.jcmd.jmod                             jdk.management.agent.jmod  jdk.unsupported.jmod
javafx.media.jmod       java.scripting.jmod       jdk.aot.jmod              jdk.incubator.jpackage.jmod  jdk.jconsole.jmod                         jdk.management.jfr.jmod    jdk.xml.dom.jmod
javafx.swing.jmod       java.security.jgss.jmod   jdk.attach.jmod           jdk.internal.ed.jmod         jdk.jdeps.jmod                            jdk.management.jmod        jdk.zipfs.jmod
javafx.web.jmod         java.security.sasl.jmod   jdk.charsets.jmod         jdk.internal.jvmstat.jmod    jdk.jdi.jmod                              jdk.naming.dns.jmod

下面以 utils 这个module为例,介绍对module的打包操作,编译产生的module结构如下:

> tree mod/utils/                                                                                                                                                                                              <<<
mod/utils/
├── module-info.class
└── org
    └── example
        └── utils
            └── StringUtils.class

打包有两种方法,一种是先打jar包,然后打jmod包,一种是直接打jmod包。

方法一 jar -> jmod

首先进入模块目录 mod/utils 目录,然后执行下面命令:

$ jar -cf ../utils.jar .                       

打完的jar包会存储在 mod 目录下,然后回到 mod 目录,执行下面命令,生成 utils.jmod

$ jmod create --class-path utils.jar utils.jmod

方法二 直接打jmod包

mod 目录下直接运行下面的命令即可生成 utils.jmod

$ jmod create --class-path utils/ utils.jmod

打完jmod包之后,可以使用 jmod list utils.jmod 查看其中的内容。

为了方便下面的演示,也对 test 模块打个包,在 mod 目录下执行下面命令

$ jmod create --class-path test --main-class org.example.test.TestStringUtils test.jmod

其中的 --main-class 用于指明module中的main函数所在的类。

使用jlink生成一个包含制定模块的JRE环境

如果你是按照顺序执行上述命令到这,现在在 mod 下应该有 utils, test两个目录, utils.jmod , test.jmod 两个文件。由于jmod是一个模块,包含 module-info.java 目录也是模块,在 mod 下就有四个模块,其中两个是重复的。为了保证下面实验的正确性,需要将两个目录或者两个文件移出 mod 目录。

mod 目录下执行下面命令

$ jlink --module-path . --add-modules test,utils --output /tmp/jlink

上面的命令会在 /tmp/jlink 目录下生成JRE环境,这个JRE环境已经包含了我们自定义的两个模块 utilstest ,执行下面的代码运行 test 模块中的 org.example.test.TestStringUtils

$ /tmp/jlink/bin/java -m test/org.example.test.TestStringUtils                                           │              An options file is a text file that contains the options and values that you would ordi‐
hello 
  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论

打赏作者

sha256sum

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值