«1. Введение

Project Jigsaw — это зонтичный проект с новыми функциями, нацеленными на два аспекта:

    введение модульной системы в язык Java и ее реализацию в исходном коде JDK и среде выполнения Java

В этой статье мы познакомит вас с проектом Jigsaw и его функциями и, наконец, завершит его простым модульным приложением.

2. Модульность

Проще говоря, модульность — это принцип проектирования, который помогает нам достичь:

    слабой связи между компонентами четких контрактов и зависимостей между компонентами скрытой реализации с использованием строгой инкапсуляции

2.1. Единица модульности

Теперь возникает вопрос, что является единицей модульности? В мире Java, особенно с OSGi, файлы JAR считались единицей модульности.

JAR-файлы помогли сгруппировать связанные компоненты вместе, но у них есть некоторые ограничения:

    явные контракты и зависимости между JAR-файлами слабая инкапсуляция элементов внутри JAR-файлов

2.2. JAR Hell

Была еще одна проблема с JAR – JAR hell. Несколько версий JAR-файлов, лежащих в пути к классам, привели к тому, что ClassLoader загрузил первый найденный класс из JAR-файла с очень неожиданными результатами.

Другая проблема с JVM, использующей путь к классам, заключалась в том, что компиляция приложения будет успешной, но приложение завершится ошибкой во время выполнения с ClassNotFoundException из-за отсутствующих JAR-файлов в пути к классам во время выполнения.

2.3. Новая единица модульности

Со всеми этими ограничениями при использовании JAR в качестве единицы модульности создатели языка Java придумали новую конструкцию языка, называемую модулями. Вместе с этим для Java запланирована совершенно новая модульная система.

3. Project Jigsaw

Основными мотивами этого проекта являются:

    создание модульной системы для языка – реализовано в JEP 261, применение ее к исходному коду JDK – реализовано в JEP 201, модульность Библиотеки JDK — реализованы в рамках JEP 200, обновляют среду выполнения для поддержки модульности — реализованы в рамках JEP 220 позволяют создавать меньшую среду выполнения с подмножеством модулей из JDK — реализованы в рамках JEP 282

Другая важная инициатива заключается в инкапсулировать внутренние API в JDK, те, что находятся под солнцем.* пакеты и другие нестандартные API. Эти API никогда не предназначались для публичного использования и никогда не планировались для обслуживания. Но мощь этих API заставила разработчиков Java использовать их при разработке различных библиотек, фреймворков и инструментов. Были предоставлены замены для нескольких внутренних API, а остальные были перемещены во внутренние модули.

4. Новые инструменты модульности

    jdeps — помогает анализировать кодовую базу для выявления зависимостей от JDK API и сторонних JAR-файлов. Также упоминается название модуля, в котором можно найти JDK API. Это упрощает модульность кодовой базы jdeprscan — помогает анализировать кодовую базу на предмет использования любых устаревших API jlink — помогает создать меньшую среду выполнения за счет объединения модулей приложения и JDK jmod — помогает в работе с jmod-файлами. jmod — это новый формат упаковки модулей. Этот формат позволяет включать собственный код, файлы конфигурации и другие данные, которые не помещаются в файлы JAR

5. Архитектура системы модулей

Система модулей, реализованная в языке, поддерживает их как конструкцию верхнего уровня, как пакеты. Разработчики могут организовывать свой код в модули и объявлять зависимости между ними в соответствующих файлах определения модулей.

Файл определения модуля, названный module-info.java, содержит:

    его имя пакеты, которые он делает общедоступными модули, которые он зависит от любых служб, которые он потребляет, любую реализацию для службы, которую он предоставляет

Последний два элемента в приведенном выше списке обычно не используются. Они используются только тогда, когда службы предоставляются и потребляются через интерфейс java.util.ServiceLoader.

Общая структура модуля выглядит так:

src
 |----com.baeldung.reader
 |     |----module-info.java
 |     |----com
 |          |----baeldung
 |               |----reader
 |                    |----Test.java
 |----com.baeldung.writer
      |----module-info.java
           |----com
                |----baeldung
                     |----writer
                          |----AnotherTest.java

«

«На приведенном выше рисунке определены два модуля: com.baeldung.reader и com.baeldung.writer. Каждое из них имеет свое определение, указанное в файле module-info.java и файлах кода, размещенных в каталогах com/baeldung/reader и com/baeldung/writer соответственно.

5.1. Терминология определения модуля

    Давайте рассмотрим некоторые термины; мы будем использовать при определении модуля (т. е. в файле module-info.java):

модуль: файл определения модуля начинается с этого ключевого слова, за которым следует его имя и определение требует: используется для указания модулей, от которых он зависит; имя модуля должно быть указано после этого ключевого слова. это означает, что любой модуль, который зависит от определения модуля, требует транзитивного \u003cимя модуля\u003e, получает неявную зависимость от экспорта \u003cимя модуля\u003e: используется для указания пакетов внутри модуля, доступных публично; имя пакета должно быть указано после открытия этого ключевого слова: используется для указания пакетов, которые доступны только во время выполнения, а также доступны для самоанализа через API-интерфейсы Reflection; это очень важно для таких библиотек, как Spring и Hibernate, которые сильно зависят от API-интерфейсов Reflection; opens также может использоваться на уровне модуля, и в этом случае весь модуль доступен во время выполнения. uses: используется для указания интерфейса службы, который использует этот модуль; имя типа, т. е. полное имя класса/интерфейса, должно быть указано после этого ключевого слова, обеспечивающего … с ​​…: они используются для указания того, что он предоставляет реализации, указанные после ключевого слова with, для интерфейса службы, указанного после ключевого слова содержит ключевое слово

6. Простое модульное приложение

Давайте создадим простое модульное приложение с модулями и их зависимостями, как показано на диаграмме ниже:

public class Student {
    private String registrationId;
    //other relevant fields, getters and setters
}

com.baeldung.student.model — это корневой модуль. Он определяет класс модели com.baeldung.student.model.Student, который содержит следующие свойства:

module com.baeldung.student.model {
    exports com.baeldung.student.model;
}

Он предоставляет другим модулям типы, определенные в пакете com.baeldung.student.model. Это достигается определением его в файле module-info.java:

public interface StudentService {
    public String create(Student student);
    public Student read(String registrationId);
    public Student update(Student student);
    public String delete(String registrationId);
}

Модуль com.baeldung.student.service предоставляет интерфейс com.baeldung.student.service.StudentService с абстрактными операциями CRUD:

module com.baeldung.student.service {
    requires transitive com.baeldung.student.model;
    exports com.baeldung.student.service;
}

Это зависит от модуля com.baeldung.student.model и делает типы, определенные в пакете com.baeldung.student.service, доступными для других модулей:

public class StudentDbService implements StudentService {

    public String create(Student student) {
        // Creating student in DB
        return student.getRegistrationId();
    }

    public Student read(String registrationId) {
        // Reading student from DB
        return new Student();
    }

    public Student update(Student student) {
        // Updating student in DB
        return student;
    }

    public String delete(String registrationId) {
        // Deleting student in DB
        return registrationId;
    }
}

Мы предоставляем еще один модуль com.baeldung.student .service.dbimpl, который обеспечивает реализацию com.baeldung.student.service.dbimpl.StudentDbService для вышеуказанного модуля:

module com.baeldung.student.service.dbimpl {
    requires transitive com.baeldung.student.service;
    requires java.logging;
    exports com.baeldung.student.service.dbimpl;
}

Зависит напрямую от com.baeldung.student.service и транзитивно от com.baeldung.student .model и его определение будет следующим:

public class StudentClient {

    public static void main(String[] args) {
        StudentService service = new StudentDbService();
        service.create(new Student());
        service.read("17SS0001");
        service.update(new Student());
        service.delete("17SS0001");
    }
}

Последний модуль — это клиентский модуль, который использует модуль реализации службы com.baeldung.student.service.dbimpl для выполнения своих операций:

module com.baeldung.student.client {
    requires com.baeldung.student.service.dbimpl;
}

И его определение:

7. Компиляция и запуск примера

  1. compile-student-model
  2. compile-student-service
  3. compile-student-service-dbimpl
  4. compile-student-client
  5. run-student-client

Мы предоставили скрипты для компиляции и запуска вышеуказанного мо dules для платформ Windows и Unix. Их можно найти в проекте core-java-9 здесь. Порядок выполнения для платформы Windows:

  1. compile-modules
  2. run-student-client

Порядок выполнения для платформы Linux довольно прост:

    В приведенных выше сценариях вы познакомитесь со следующими двумя аргументами командной строки:

—module -source-path – путь к модулю

Java 9 отказывается от концепции пути к классам и вместо этого вводит путь к модулю. Этот путь является местом, где можно обнаружить модули.

Мы можем установить это с помощью аргумента командной строки: —module-path.

Чтобы скомпилировать несколько модулей одновременно, мы используем –module-source-path. Этот аргумент используется для указания местоположения исходного кода модуля.

8. Модульная система, применяемая к исходному коду JDK

«Каждая установка JDK поставляется с src.zip. Этот архив содержит базу кода для JDK Java API. Если вы извлечете архив, вы найдете несколько папок, некоторые из которых начинаются с java, несколько с javafx и остальные с jdk. Каждая папка представляет модуль.

Модули, начинающиеся с java, являются модулями JDK, те, которые начинаются с javafx, являются модулями JavaFX, а другие, начинающиеся с jdk, являются модулями инструментов JDK.

Все модули JDK и все пользовательские модули неявно зависят от модуля java.base. Модуль java.base содержит часто используемые API-интерфейсы JDK, такие как Utils, Collections, IO, Concurrency и другие. График зависимостей модулей JDK:

Вы также можете посмотреть определения модулей JDK, чтобы получить представление о синтаксисе их определения в файле module-info.java.

9. Заключение

В этой статье мы рассмотрели создание, компиляцию и запуск простого модульного приложения. Мы также видели, как исходный код JDK был модульным.

Есть еще несколько интересных функций, таких как создание меньшего времени выполнения с помощью инструмента компоновщика — jlink и создание модульных jar-файлов среди других функций. Мы подробно познакомим вас с этими функциями в следующих статьях.

Project Jigsaw — это огромное изменение, и нам придется подождать и посмотреть, как оно будет воспринято экосистемой разработчиков, в частности, создателями инструментов и библиотек.