Java 模块化编程

现在,Java每半年发布一个版本,知道为什么这么快吗?这主要得益于Java的模块化技术。 换作以前,时间得以【年】计算。 从本质上讲,模块化(modularization) 是指将系统分解成独立且相互连接的模块的行为。模抉( module) 是包含代码的可识别部分,使用元数据来描述模块与其它模块之间的关系。在Java 模块系统之前, JDK 的运行时库由一个庞大的rt.jar 所组成,其大小超过60MB ,包含了Java 大部分运行时类: 即Java 平台的最终载体。为了获得一个灵活且符合未来发展方向的平台, JDK 团队着手对JDK 进行模块化。模块化可以将jar声明为模块,它将在其自己的隔离类加载器中运行,该类加载器以OSGI方式从其他类似的模块类加载器读取类文件。这将允许同一版本的Jar的多个版本共存。 毕竟Java太庞大,模块化过程持续了几年时间,最终随Java9发布.

Java9模块化系统出现以前Java模块化的核心三原则:

  • 组合使用package和访问修饰符(public,protected,private)可以实现类型封装
  • 定义良好的接口, 也就是使用interface关键字公开公共接口
  • 显式依赖,也就是使用import关键字

Java9之后模块化

  • JDK 由大约90个模抉组成,而不是一个整体库。与可由自己创建的应用程序模块不同的是,平台模块是JDK的一部分。从技术上讲,平台模块和应用模块之间没有任何技术区别。每个平台模块都构成了JDK 的一个定义良好的功能块,从日志记录到XML 支持。所有模块都显式地定义了与其他模块的依赖关系。

模块工作原理

  • 什么是模块,它是如何定义的?模块拥有一个名称,并对相关的代码以及可能的其他资源进行分组,使用一个模块描述符进行描述。模块描述符保存在一个名为 module-info.java 的文件中, 为此我们先创建一个Project,然后在Project下创建两个module, 如图所示:

    1. 创建一个Project jfxdemo

    1. 在这个jfxdemo工程下创建二个javafx module, 以下是第一个模块demo1

    1. 第二个模块 demo2

  • 创建完成后可以发现,每个module下都有一个模块描述文件 module-info.java

    此处我们使用javafx是为了后续开发游戏做准备,同时Javafx库默认情况下不能直接在应用程序中使用,必须使用模块描述文件来导入

  • 查看demo1模块的module-info.java文件

    1
    2
    3
    4
    5
    6
    module com.example1.demo1 {
    requires javafx.controls;
    requires javafx.fxml;
    opens com.example.demo1 to javafx.fxml;
    exports com.example1.demo1;
    }
    • module-info.java文件中的第一行表示,当前应用是一个模块,模块名为 com.example.demo1, 模块名称虽然随便写个字符串也可以,但最好以域名作为基础,也就是使用反向DNS符号来确保模块的唯一性,后面紧跟着项目名称,建议尽量使用自己的域名。模块都位于一个全局命名空间中,因此,模块名称必须唯一。

    • requires 表示一个依赖关系,此是表示当前应用程序需要使用到javafx.controls模块以及javafx.xml模块,如果将这两句代码删除,那么在需要引用这两个模块的类中将会提示错误, 如下图所示 :

      • 注释了requires javafx.fxml;
      • 在HelloApplication中将无法使用

    • exports的意思是来自com.example.demo1模块的单个包被导出到其它模块

    • opens表示com.example.demo1中的类型可用于深度反射

模块化开发

在demo2模块中需要使用demo1模块中的定义的类

  1. demo1模块中的module-info.java配置如下:
1
2
3
4
5
6
module com.example.demo1 {
requires javafx.controls;
requires javafx.fxml;
opens com.example.demo1 to javafx.fxml;
exports com.example.demo1;
}
  1. 在module demo2中的pom.xml文件中加入demo1的依赖
1
2
3
4
5
<dependency>
<groupId>com.example</groupId>
<artifactId>demo1</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
  1. 在demo2中要使用demo1模块中的类,则需要在demo2 模块中修改module-info.java文件
1
2
3
4
5
6
7
8
module com.example.demo2 {
requires javafx.controls;
requires javafx.fxml;
requires com.example.demo1; //引入名称为com.example.demo1的模块

opens com.example.demo2 to javafx.fxml;
exports com.example.demo2;
}
  1. 在demo2模块中的HelloApplication中使用demo1模块中的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.demo2;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import com.example.demo1.*; //此处引用demo1模块API


public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}

public static void main(String[] args) {
launch();
}
}
  1. 如果在demo1模块中新建两个包,demo2模块中需要引用则要在demo1模块的module-info.java 文件中加入exports

demo2模块中的HelloApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.example.demo2;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import com.example.demo1.*;
import com.example.demo1.service.*;
import com.example.demo1.dao.*;


public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}

public static void main(String[] args) {
launch();
}
}

上面的内容中使用了java11进行模块化开发的第一步,也就是手动编写自己的第一个模块,而不仅仅是查看JDK中现有的模块。有了这部分经验后,后续就可以创建更加复杂的模块