Статьи

Java 9, Jigsaw, JPMS и модули: личное исследование

Java 9 откладывалась так много раз из-за Project Jigsaw, и вы, возможно, много слышали о модулях, модульности и других вещах, так что же это такое? Какого черта модульность и что мы подразумеваем под модульной платформой? Модульная система платформы Java (JPMS)? Это будет революция в экосистеме Java?
Этот пост — мое исследование самого важного, что произошло с JDK, системы модулей. Я объясню, что такое модульность, зачем она вам нужна и как вы можете создать свой модульный проект.

Что почему:

Ремонтопригодность является одной из наиболее важных проблем в разработке и развитии программного обеспечения. Мы хотим, чтобы кодовая база была слабосвязанной, очень связной, чрезвычайно удобочитаемой и понятной в одно мгновение. Мы разрабатываем наши классы и организуем их в пакеты. Пока все хорошо, но когда у нас есть сотни пакетов, зависимости между ними не видны одним выстрелом. Поэтому нам нужно нечто большее, чем пакеты, чтобы организовать нашу кодовую базу и сделать ее более понятной.
Другая проблема — путь к классу Java и то, как он запускает наши коды. Все jar-классы и библиотеки объединены в classpath. Когда эти файлы JAR имеют несколько версий класса во время выполнения, Java ClassLoader может загрузить только одну версию этого класса, таким образом, возникает двусмысленность в отношении того, как ваша программа будет работать, и неоднозначность — это плохо. Эта проблема настолько частая, что называется « JAR Hell ».

Другая проблема с classpath заключается в том, что он не следует принципу «Fail First» У вас могут отсутствовать классы, которые существуют в пути к классам, но его нет в производственной среде. Пока исключение JavaClassDefError во время выполнения , вы не можете быть уверены, что отсутствует. Наконец, большая проблема с classpath — инкапсуляция. Каждый класс на пути к классам имеет доступ друг к другу, и это нарушение инкапсуляции. Мы хотим скрыть наши внутренние API, и поэтому нам необходим еще один уровень инкапсуляции ( «Сильная инкапсуляция» ) и контроль доступа к нашим классам в наших пакетах.

Модули собираются исправить эти проблемы. Что такое модуль? Модуль имеет имя, он группирует связанный код и является автономным. Модуль явно описывает, что ему нужно от других модулей и какая его часть видна другим модулям. Таким образом, зависимости между модулями предельно ясны. У нас есть строгая инкапсуляция, которая означает, что мы можем скрыть наши внутренние API, и, наконец, теперь мы следуем принципу «Сначала потерпеть неудачу», поэтому при наличии отсутствующего модуля или конфликта вы получите ошибку.


Модуляризация JDK позволяет разработчикам JDK справляться с огромной сложностью. Когда вы пишете крошечное и простое приложение, которое не использует RMI, CORBA, JavaEE и другие вещи, зачем вам нужна полная, огромная и тяжелая среда выполнения Java? Разве не мудрее иметь ваш Runtime Image, который содержит только те модули, которые вам нужны? Теперь с модульной платформой это возможно.
Вот как теперь выглядит JDK. Внизу у нас есть модуль « java.base », от которого неявно или явно зависит любой другой модуль. Как видите, этот граф зависимостей является DAG, что означает, что циклическая зависимость не допускается.

На рисунке ниже показано, что представляет собой модуль. Каждый модуль имеет дескриптор модуля с именем «module-info.java».

В файле module-info.java вы описываете название вашего модуля, что требуется для работы и какие пакеты видны за пределами этого модуля. Например, вы можете видеть, какие пакеты java.sql экспортирует (делает видимыми) и какие модули ему требуются.

Итак, в простейшей форме, module-info.java выглядит как на картинке ниже:

В следующем разделе я покажу, как вы можете работать с этими модулями и создавать свои модули.

Как:

Прежде всего, вам необходимо скачать и установить Java 9. Вы можете найти его здесь .

Версия Java

1
2
3
4
$ java -version
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode

Давайте создадим проект в IntelliJ IDEA:

На рисунке ниже показано, как создать модуль:

После создания модуля вам необходимо создать файл module-info.java внутри src:

Я построил проект, который имеет два модуля: «com.mhrimaz.gui» и «com.mhrimaz.logic». Вы можете увидеть структуру проекта на изображении:

В модуле com.mhrimaz.logic у меня есть два класса, которые называются «InternalGreeting» и «Greeting».

InternalGreeting.java

1
2
3
4
5
6
7
package com.mhrimaz.logic.internals;
  
public class InternalGreeting {
    public static String sayHello(String name){
        return "Hello, This Greeting is internal dear "+ name;
    }
}

Greeting.java

1
2
3
4
5
6
7
package com.mhrimaz.logic;
  
public class Greeting {
    public static String sayHello(String name){
        return "Hello, " + name;
    }
}

Модуль-info.java com.mhrimaz.logic, является следующим:

1
2
3
module com.mhrimaz.logic {
    exports com.mhrimaz.logic;
}

Это означает, что пакет com.mhrimaz.logic (это имя пакета, а не имя модуля не путайте) виден вне этого модуля, но пакет com.mhrimaz.logic.internals не виден.

Файл MianApplication представляет собой простую программу JavaFX:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.mhrimaz.gui;
  
import com.mhrimaz.logic.Greeting;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
  
public class MainApplication extends Application {
  
  
    @Override
    public void start(Stage primaryStage) throws Exception {
        Label label = new Label(Greeting.sayHello("Hossein"));
        StackPane pane = new StackPane();
        pane.getChildren().add(label);
  
        Scene scene = new Scene(pane);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

Похоже, что этот пакет не должен экспортировать ничего, для этого ему нужны только javafx.base и javafx.controls, а для использования класса Greeting нам также необходим com.mhrimaz.logic. Информация о модуле com.mhrimaz.gui выглядит следующим образом:

1
2
3
4
5
module com.mhrimaz.gui {
    requires javafx.base;
    requires javafx.controls;
    requires com.mhrimaz.logic;
}

Когда мы запустим наше приложение, мы получим исключение:

1
2
3
Caused by: java.lang.IllegalAccessException: class com.sun.javafx.application.LauncherImpl
(in module javafx.graphics) cannot access class com.mhrimaz.gui.MainApplication
(in module com.mhrimaz.gui) because module com.mhrimaz.gui does not export com.mhrimaz.gui to module javafx.graphics

очевидно, это говорит о том, что нам нужно экспортировать пакет com.mhrimaz.gui. Это означает, что javafx.graphics использует MainApplication для передачи ему Stage, и вам нужно экспортировать свой пакет в javafx.graphics (Примечание: вы можете экспортировать только пакет в конкретный модуль или экспортировать его во все модули)
Теперь модуль-info.java выглядит следующим образом:

1
2
3
4
5
6
module com.mhrimaz.gui {
    requires javafx.base;
    requires javafx.controls;
    requires com.mhrimaz.logic;
    exports com.mhrimaz.gui to javafx.graphics;
}

И результат выглядит как ошибка в реализации JavaFX в Java 9, но это наш результат:

На этом история не заканчивается. Существует множество деталей о модулях, зависимостях между ними, которые вы можете прочитать в книге Java 9 Revealed или Java 9 Modularity .

Ссылка: Java 9, Jigsaw, JPMS и модули: личное исследование от нашего партнера по JCG Мохаммада Хоссейна Римаса в блоге Хоссейна Римаса .