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:
- компиляция-модель студента
- компиляция-студент-сервис
- compile-student-service-dbimpl
- компиляция-студент-клиент
- запуск-студент-клиент
Порядок выполнения для платформы Linux довольно прост:
- компиляция-модули
- запуск-студент-клиент
В приведенных выше сценариях вы познакомитесь со следующими двумя аргументами командной строки:
- –модуль-источник-путь
- –модуль-путь
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 .