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

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

1. Вступление

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

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

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

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

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

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

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

  • четкие контракты и зависимости между компонентами

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

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

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

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

  • явные контракты и зависимости между JAR

  • слабая инкапсуляция элементов в JAR

2.2. JAR Ад

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

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

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

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

3. Проект Jigsaw

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

  • create a module system for the language - реализовано вJEP 261

  • apply it to the JDK source - реализовано вJEP 201

  • modularize the JDKlibraries - реализовано вJEP 200

  • update the runtime to support modularity - реализовано вJEP 220

  • be able to create smaller runtime with a subset of modules from JDK - реализовано вJEP 282

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

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

  • jdeps - помогает анализировать базу кода для определения зависимостей от JDK API и сторонних JAR. В нем также упоминается название модуля, в котором можно найти API JDK. Это облегчает модульную основу кода

  • jdeprscan - помогает анализировать базу кода для использования любых устаревших API

  • jlink - помогает уменьшить время выполнения за счет объединения модулей приложения и JDK.

  • jmod - помогает в работе с jmod файлами. jmod - новый формат для упаковки модулей. Этот формат позволяет включать собственный код, файлы конфигурации и другие данные, которые не помещаются в файлы JAR.

5. Модульная архитектура системы

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

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

  • его имя

  • пакеты, которые он делает доступными публично

  • модули это зависит от

  • любые услуги, которые он потребляет

  • любая реализация для предоставляемого сервиса

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

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

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

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

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

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

  • module: файл определения модуля начинается с этого ключевого слова, за которым следует его имя и определение

  • requires: используется для обозначения модулей, от которых он зависит; имя модуля должно быть указано после этого ключевого слова

  • transitive: указывается после ключевого словаrequires; это означает, что любой модуль, который зависит от модуля, определяющегоrequires transitive <modulename>, получает неявную зависимость от <modulename>

  • exports: используется для обозначения общедоступных пакетов внутри модуля; после этого ключевого слова необходимо указать имя пакета

  • opens: используется для обозначения пакетов, доступных только во время выполнения, а также доступных для самоанализа через API Reflection; это очень важно для таких библиотек, как Spring и Hibernate, которые сильно зависят от API Reflection; opens также можно использовать на уровне модуля, и в этом случае весь модуль доступен во время выполнения

  • uses: используется для указания интерфейса службы, который использует этот модуль; имя типа, то есть полное имя класса / интерфейса, должно быть указано после этого ключевого слова

  • provides … with ..: они используются, чтобы указать, что он предоставляет реализации, указанные после ключевого словаwith, для интерфейса службы, указанного после ключевого словаprovides

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

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

image

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

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

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

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

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

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

Мы предоставляем другой модульcom.example.student.service.dbimpl, который обеспечивает реализациюcom.example.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 sutdent in DB
        return student;
    }

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

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

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

Последний модуль - это клиентский модуль, который использует модуль реализации службыcom.example.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.example.student.client {
    requires com.example.student.service.dbimpl;
}

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

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

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

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

  3. компиляции студент-сервис-dbimpl

  4. компиляции студент-клиент

  5. вводного студент-клиент

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

  1. компиляции модулей

  2. вводного студент-клиент

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

  • –Module-source-path

  • –Module-path

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

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

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

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

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

image

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

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

image

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

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

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

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

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

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