Skip to content

模块定义

虽然清楚了JDK1.9之后模块的基本概念,但是该如何去定义模块以及模块该如何执行呢?模块的本质和包是非常类似的,都属于一个文件目录,但是这个文件目录如果要想生效,那么就必须有所描述,而这个描述的文件就称为一个"module-info.java"文件

1. 模块介绍

模块就是代码和数据的封装体。模块的代码被组织成多个包,每个包中包含Java类和接口:模块的数据则包括资源文件和其他静态信息。 Java9模块的重要特征是在其工件(artifct)的根目录中包含了一个描述模块的 module-inf.class文件。工件的格式可以是传统的JAR文件或是Java9新增的JMOD文件。这个文件由根目录中的源代码文件module-info.java 编译而来。该模块声明文件可以描述模块的不同特征。
在module-info.java文件中,可以用新的关键词module来声明一个模块。模块名称应全局唯一,不可重复。加上 open关键词表示模块内的所有包都允许通过Java反射访问,模块声明体内不再允许使用opens语句。

2. 模块相关命令参数

Java 9引入了一系列新的参数用于编译和运行模块

2.1 查看jar包是否支持模块化

cmd
jar -d -f attendance-0.0.1-SNAPSHOT.jar
com.rocket.attendance@0.0.1-SNAPSHOT jar:file:///F:/Code/jmods-demo/attendance/target/attendance-0.0.1-SNAPSHOT.jar!/module-info.class
requires com.rocket.sys
requires java.base mandated
contains com.rocket.attendance
main-class com.rocket.attendance.DailyWork

2.2 --add-exports参数

--add-exports 访问内部API
如果旧代码迁移到JDK9+, 编译报package x.x.x is not visible的错误时,是因为旧代码访问了模块的内部API;为了能够访问内部API,需要在编译时加上--add-exports java.xx/x.x.x=ALL-UNNAMED

2.3 -add-open参数

--add-open 反射访问内部API
但是,这种方式只是在编译期起作用;那些在x在运行时才知道访问了内部API的问题需要新的方式解决; 错误异常通常是 java.lang.reflect.InaccessibleObjectException..
解决方式是运行时加上--add-opens x.x/x.x.x=All-UNNAMED通过反射调用的类和方法。

2.4 -add-modules参数

--add-modules 添加依赖模块
如项目依赖Java EE的相关xml代码, 编译和运行时就需要添加对应的模块javac --add-moudles java.xml.bind

2.5 --patch-modules参数

当迁移过程中讨论拆包装,我们看到了一个使用注释的项目的例子 @ 生成(从java.xml.ws.annotation模块)和 @ 非空(从JSR 305实现)。我们发现了三件事: 两个注释都在javax .annotation包中,因此创建了一个分割,需要手动添加模块,因为它是一个Java EE模块,这样做会使拆分包的JSR 305部分不可见

3. 申明模块

  1. 创建maven工程jmods-demo, 并创建两个子模块sys和attendance:
    Alt text
  2. 在sys模块中的src/main/java目录下创建module-info.java文件:
java
module com.rocket.sys {

}

并编写UserInfo.java和PrivilegeInfo.java文件:

java
package com.rocket.sys;

public class UserInfo {

    public void login(){
        System.out.println("login ....");
    }
}
java
package com.rocket.sys;

public class PrivilegeInfo {
    public void checkPrivilege(){
        System.out.println("checkPrivilege....");
    }
}
  1. 编译代码:
cmd
javac -d target/classes -sourcepath src/main/java src/main/java/com/rocket/sys/*.java

执行效果:
Alt text

警告

在Maven工程中,每个模块只能有一个module-info.java文件,且这个文件必须位于模块的根目录下。

4. 模块暴露

使用exports关键字,导出模块内的包(允许其他模块直接import使用),一次导出一个包,如果需要导出多个包,需要多次声明。如果需要定向导出,可以使用to关键词,后面加上模块列表(逗号分隔)。
修改module-info.java:

java
module com.rocket.sys {
    exports com.rocket.sys;
}