Рубрики
Без рубрики

Введение в проект Jigsaw

Узнайте, как создать, скомпилировать и запустить простое модульное приложение, используя новые инструменты JDK, представленные в виде головоломки проекта.

Автор оригинала: baeldung.

1. введение

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

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

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

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

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

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

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

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

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

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

2.2. КУВШИННЫЙ ад

Была еще одна проблема с банками – ад с БАНКАМИ. Несколько версий банок, лежащих на пути к классам, привели к тому, что ClassLoader загрузил первый найденный класс из банки с очень неожиданными результатами.

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

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

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

3. Проектная головоломка

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

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

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

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

  • jdeps – помогает в анализе базы кода для определения зависимостей от API JDK и сторонних JAR-файлов. В нем также упоминается имя модуля, в котором можно найти API JDK. Это облегчает модуляризацию базы кода
  • 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) :

  • module : файл определения модуля начинается с этого ключевого слова, за которым следуют его имя и определение
  • требуется : используется для указания модулей, от которых он зависит; имя модуля должно быть указано после этого ключевого слова
  • transitive : указывается после ключевого слова requires ; это означает, что любой модуль, который зависит от модуля, определяющего requires transitive , получает неявную зависимость от < modulename>
  • exports : используется для указания пакетов в модуле, доступных публично; имя пакета должно быть указано после этого ключевого слова
  • открывает : используется для указания пакетов, доступных только во время выполнения, а также доступных для самоанализа через API отражения; это весьма важно для библиотек, таких как Spring и Hibernate, которые сильно полагаются на API отражения; открывает также может использоваться на уровне модуля, и в этом случае весь модуль доступен во время выполнения
  • использует : используется для указания интерфейса службы, используемого этим модулем; имя типа, т. е. полное имя класса/интерфейса, должно быть указано после этого ключевого слова
  • предоставляет … с .. .: они используются для указания, что он предоставляет реализации, идентифицированные после ключевого слова с , для интерфейса службы, идентифицированного после ключевого слова предоставляет .

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

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

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

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

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

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

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

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.model и делает типы, определенные в пакете com.baeldung.student.service доступными для других модулей:

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

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

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 и транзитивно от com.baeldung.student.model и его определение будет:

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.dbimpl для выполнения своих операций:

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");
    }
}

И его определение таково:

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

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

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

  1. компиляция-модель студента
  2. компиляция-студент-сервис
  3. compile-student-service-dbimpl
  4. компиляция-студент-клиент
  5. запуск-студент-клиент

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

  1. компиляция-модули
  2. запуск-студент-клиент

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

  • –модуль-источник-путь
  • –модуль-путь

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

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

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

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

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

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

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

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

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

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

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

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

Код, используемый в этой статье, можно найти на GitHub .