В статье описываются изменения, требуемые для сборки дистрибутива с поддержкой модульности. Сборка дистрибутива производится в операционных системах Windows, macOS и Linux.
Полный список новых функций JDK 9 впечатляет. Проект Jigsaw является главной целью версии 9. Предметом данной статьи является составная часть проекта Jigsaw — JEP 275: Modular Java Application Packaging и связанные с ним:
- JEP 282: jlink: The Java Linker
- JEP 220: Modular Run-Time Images
- JEP 261: Module System
- JEP 200: The Modular JDK
В JDK входит утилита командной строки Java Packager, позволяющая выполнять компиляцию, сборку, создание цифровой подписи и дистрибутивов Java-приложений. Функции, которые выполняет Java Packager, начиная с JDK 7 Update 6, доступны и в виде задач (tasks) для Ant. В официальной документации они имеют наименование JavaFX Ant Tasks, т.к. ранее предназначались в первую очередь для JavaFX-приложений.
JEP 275: Modular Java Application Packaging направлен на интеграцию модульности в Java Packager, в том числе для создания образа JRE, включающего только те модули, которые использует приложение. JavaFX Ant Tasks так же могут быть вызваны и из Maven, доминирующего на сегодня инструмента сборки, посредством Maven AntRun Plugin.
В экспериментах использовались:
- операционные системы Windows 10, macOS Sierra 10.12.4, Ubuntu 16.04.2 LTS;
- JDK 9 Early Access (Build 163 на момент написания статьи) для всех трёх операционных систем;
- Apache Maven 3.3.9;
- Inno Setup 5.5.9;
- IDE с поддержкой Java 9 (например, IntelliJ IDEA 2017.1).
Код примера доступен на GitHub. Для компиляции и сборки дистрибутива используется Maven. Проект состоит из четырёх частей (модулей в терминологии Maven):
- Консольное (console) приложение без использования модулей (модулей Java 9);
- Консольное приложение с использованием модулей;
- Настольное (desktop) приложение без использования модулей;
- Настольное приложение с использованием модулей.
Сборка дистрибутивов выполняется во всех трёх операционных системах.
Сборка дистрибутива без использования модулей
Процесс сборки дистрибутива состоит из нескольких шагов:
- Компиляция и создание файла JAR;
- Формирование образа JRE;
- Создание дистрибутива, включающих JAR, образ JRE и вспомогательные исполняемые файлы для запуска.
Для создания дистрибутивов в macOS и Linux ничего дополнительно не нужно (кроме JDK и Maven). В операционной системе Windows предварительно необходимо установить Inno Setup или WiX Toolset. Далее подразумевается, что используется Inno Setup.
Создание файла JAR при использовании Maven проще выполнить обычным способом, т.е. используя Apache Maven JAR Plugin (другой способ — использование <fx:jar>). В манифесте (файле
MANIFEST.MF
) файла JAR, используемом для сборки дистрибутива немодульного (без использования модулей Java 9) приложения, обязательно должен быть указан Main-Class
. По этой причине использование вышеуказанного плагина должно быть явно задано с указанием в конфигурации основного класса.Описание использования задачи <fx:deploy> в файле
pom.xml
:Запуск компиляции и сборки дистрибутива в Windows и macOS:
mvn clean package -P native-deploy
Запуск компиляции и сборки дистрибутива в Linux (дополнительно создаётся файл архива
tar.gz
):mvn clean package -P native-deploy,tar-gz
Файлы созданного дистрибутива с расширением
exe
, dmg
располагаются в каталоге <module name>/target/deploy/native
, с расширением tar.gz
— в каталоге <module name>/target
.Добавление использования модулей
На этапе формирования образа JRE неявно будет задействован jlink, которому нужно передать информацию об используемых модулях.
Для определения, какие модули Java использует приложение, должна быть вручную запущена утилита jdeps. В версии 9 она дополнительно показывает используемые модули.
jdeps -s <name>.jar
Первое приложение (console) использует единственный модуль java.base, второе (desktop) — модули java.base и java.desktop (вместе с транзитивными модулями, т.к. java.desktop сам зависит от других модулей).
Для первого приложения требуется добавить файл
module-info.java
, содержащий (указание базового модуля java.base необязательно):module console.modular {
}
Файл
module-info.java
для второго приложения должен содержать:module desktop.modular {
requires java.desktop;
}
В файле
pom.xml
появляются обязательные параметры пути к файлам модулей (путь к стандартным модулям $JAVA_HOME/jmods
указывать необязательно) и наименования модуля приложения. Включать в качестве ресурса JAR приложения становится излишним, т.к. он уже включён в виде модуля в образ JRE.Запуск компиляции и сборки аналогичен ранее указанному.
Сравнение результатов
Размер в мегабайтах образа JRE в составе дистрибутива и собственно файла дистрибутива, созданных с использованием Java Packager:
Приложение | Windows | macOS | Linux | |||
---|---|---|---|---|---|---|
образ JRE | дистрибутив (exe) | образ JRE | дистрибутив (dmg) | образ JRE | дистрибутив (tar.gz) | |
console-nonmodular | 165 | 36 | 181 | 69 | 204 | 72 |
desktop-nonmodular | 165 | 36 | 181 | 69 | 204 | 72 |
console-modular | 35 | 9 | 35 | 13 | 44 | 16 |
desktop-modular | 72 | 18 | 75 | 29 | 85 | 31 |
Размер в мегабайтах образа JRE, созданного при ручном запуске jlink с наиболее оптимальным набором параметров (недоступном из Java Packager):
jlink --module-path <module path> --add-modules <modules> --output <directory> --compress=2 --no-header-files --no-man-pages --strip-debug
Приложение | Windows, образ JRE | macOS, образ JRE | Linux, образ JRE |
---|---|---|---|
console-modular | 22 | 21 | 29 |
desktop-modular | 38 | 39 | 49 |
Сильнее всего размер образа JRE уменьшился на macOS:
- на 81% скриптом сборки при использовании Java Packager;
- на 88% при использовании jlink.
Выводы
Значительное уменьшение размеров дистрибутива достигается относительно легко для простых проектов. В случае более сложных проектов, по крайней мере на предварительной версии JDK 9, для решения возникающих проблем может потребоваться изучение кода Java Packager.
На проходящих 4 апреля (JBreak 2017 в Новосибирске) и 7-8 апреля (JPoint 2017 в Москве) конференциях можно будет послушать доклады о других интересных возможностях Java 9 и следующих версий Java:
- Functional Reactive with Core Java9 (Sven Ruppert)
- Going Native: Foreign Functions on the JVM (Charles Nutter)
- Техники векторизации кода в JVM (Владимир Иванов)
- Повесть о том, как один инженер HTTP/2 Client разгонял (Сергей Куксенко)
- Java 9 Модули. Почему не OSGi? (Никита Липский)
- «Преждевременная» компиляция — это нормально? (Дмитрий Чуйко)
- Java Puzzlers NG S02: Всё чудесатее и чудесатее (Тагир Валеев, Барух Садогурский)
Комментарии (6)
dbelob
31.03.2017 11:05+2Спасибо за простой и понятный пример. Шикарно что все про что писали есть в виде maven сборки!
Спасибо! Постарался максимально упростить пример. В процессе пришлось изучать код Java Packager (даже не один билд).
Жаль что JEP 295 для AOT пока нельзя применить
Кстати, на эту тему будет доклад Дмитрия Чуйко «Преждевременная» компиляция — это нормально? (в Новосибирске) и недавно на JUG.ru был его же двухчасовой доклад Ahead-of-Time компиляция для HotSpot JVM (я смотрел видео, но не помню, упоминал ли он про поддержку модульности).igor_suhorukov
31.03.2017 17:22+1Респект, в свежих сборках сложно обойтись без ковыряния в исходниках! Да, в докладе Дмитрия он говорил про jigsaw и загрузку so в linux 86x64 для пакетов грааля и java.base
dbelob
31.03.2017 17:42+1… то есть в оракловском AOT модульность поддерживается, как и предполагалось.
f0stergas
31.03.2017 17:37+1Спасибо за статью. Вопрос может касается только косвенно, но интересно: если я правильно понимаю, в Java 9 теперь class-loader теперь будет загружать только классы, которые указаны в зависимостях, а не весь rt.jar, что ускорит запуск приложений?
dbelob
31.03.2017 17:56+1В Java 9 появляется возможность включить в дистрибутив только те модули (содержащие только те классы), которые приложение использует.
Файлrt.jar
и в JRE, и в JDK теперь отсутствует вообще. Вместо него в каталогеlib
имеется файлmodules
. В JRE и JDK он содержит все модули (все классы). При сборке собственного дистрибутива он создаётся и содержит только используемые приложением модули.
igor_suhorukov
Спасибо за простой и понятный пример. Шикарно что все про что писали есть в виде maven сборки!
Жаль что JEP 295 для AOT пока нельзя применить(и это не его цель на данном этапе) для создания своей бинарной сборки приложения. Размеры дистрибутива и время запуска приложения были бы еще меньше!