Когда ваше приложение выходит за рамки дюжины строк кода, вам, вероятно, следует разделить код на несколько классов. На данный момент вопрос заключается в том, как их распределить. В Java классическим форматом является Java ARchive, более известный как JAR. Но реальные приложения, вероятно, зависят от других JAR.
Цель этого поста – описать способы создания автономных исполняемых JAR-файлов, также известных как uber-JAR или fat JAR.
Что такое исполняемый файл JAR?
JAR – это просто набор файлов классов. Чтобы быть исполняемым, его файл META-INF/MANIFEST.MF
должен указывать на класс, который реализует метод main()
. Вы делаете это с помощью атрибута Main-Class
. Вот пример:
Main-Class: path.to.MainClass # 1
Основной класс
имеет/| статический main(String… аргументы)способ
Обработка пути к классу
Большинство приложений зависят от существующего кода. Java предоставляет концепцию classpath . Путь к классу – это список элементов пути, которые среда выполнения будет просматривать, чтобы найти зависимый код. Когда запускаются классы Java , вы определяете путь к классу с помощью параметра командной строки -cp
:
java -cp lib/one.jar;lib/two.jar;/var/lib/three.jar path.to.MainClass
Среда выполнения Java создает путь к классу путем объединения всех классов из всех ссылочных JAR и добавления основного класса.
Новые проблемы возникают при распределении банок, которые зависят от других банок:
- Вам нужно определить одни и те же библиотеки в одной и той же версии
Что еще более важно, аргумент
-cp
не работает с JARs . Чтобы ссылаться на другие JAR, путь к классу должен быть задан в манифесте JAR с помощью атрибутаClassPath
:По этой причине вам необходимо поместить банки в одно и то же местоположение, относительное или абсолютное, в целевой файловой системе в соответствии с манифестом. Это подразумевает, что сначала нужно открыть JAR и прочитать манифест.
Одним из способов решения этих проблем является создание уникального модуля развертывания, который содержит классы из всех JAR и который может быть распространен как один артефакт. Существует несколько вариантов создания таких банок:
- Плагин для сборки
- Плагин Shade
- Плагин Spring Boot (для проектов Spring Boot)
Плагин сборки Apache
Плагин Assembly для Maven позволяет разработчикам объединять выходные данные проекта в единый распространяемый архив, который также содержит зависимости, модули, документацию сайта и другие файлы.
Одним из правил проектирования Maven является создание одного артефакта для каждого проекта. Есть исключения например Артефакты Javadocs и исходные артефакты, но в целом, если вам нужно несколько артефактов, вам нужно создать один проект для каждого артефакта. Идея плагина Assembly заключается в том, чтобы обойти это правило.
Плагин сборки опирается на определенный assembly.xml |/конфигурационный файл. Это позволяет вам выбирать, какие файлы будут включены в артефакт. Обратите внимание, что конечный артефакт не обязательно должен быть JAR: файл конфигурации позволяет вам выбирать между доступными форматами
например, zip, war и т.д.
Плагин управляет распространенными вариантами использования, предоставляя предварительно определенные сборки. Среди них – распространение автономных банок. Конфигурация выглядит следующим образом:
maven-assembly-plugin jar-with-dependencies ch.frankel.blog.executablejar.ExecutableJarApplication single package
- Обратитесь к предварительно определенной автономной конфигурации JAR
- Установите основной класс для выполнения
- Выполните
единственную
цель - Привяжите цель к
package
phase т.е. после исходного JAR был построен
Запуск mvn package
приводит к появлению двух артефактов:
<имя>-<версия>.jar
<имя>-<версия>-с- dependencies.jar
Первый JAR имеет то же содержимое, что и тот, который был бы создан без плагина. Второй – это автономная БАНКА. Вы можете выполнить это следующим образом:
java -jar target/executable-jar-0.0.1-SNAPSHOT.jar
В зависимости от проекта, он может быть выполнен успешно … или нет. Например, в примере проекта Spring Boot произошел сбой со следующим сообщением:
%d [%thread] %-5level %logger - %msg%n java.lang.IllegalArgumentException: No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.
Причина в том, что разные банки предоставляют разные ресурсы по одному и тому же пути например META-INF/spring.factories
. Плагин следует стратегии выигрыша при последней записи. Порядок основан на названии банки.
С помощью Assembly вы можете исключать ресурсы, но не объединять их . Когда вам нужно объединить ресурсы, вы, вероятно, захотите вместо этого использовать плагин Apache Shade.
Плагин Apache Shade
Плагин Assembly является универсальным; плагин Shade фокусируется исключительно на задаче создания автономных банок.
Этот плагин предоставляет возможность упаковывать артефакт в uber-jar, включая его зависимости, и затенять – то есть переименовывать – пакеты некоторых зависимостей.
Плагин основан на концепции transformers : каждый трансформатор отвечает за обработку одного-единственного типа ресурса. Трансформатор может копировать ресурс как есть, добавлять статическое содержимое, объединять его с другими и т.д.
В то время как вы можете разработать трансформатор, плагин предоставляет набор готовых трансформаторов:
Предотвращает дублирование лицензий | ApacheLicenseResourceTransformer – преобразователь |
Готовит объединенное УВЕДОМЛЕНИЕ | Апаченотический преобразователь ресурсов |
Добавляет содержимое к ресурсу | Добавляющий преобразователь |
Агрегаты Сплетения components.xml | КомпонентыXmlResourceTransformer |
Предотвращает включение соответствующих ресурсов | Не включайте преобразователь источника |
Объединяет модули Apache Groovy extends | Заводной Трансформатор Ресурсов |
Добавляет файлы из проекта | Включает в себя преобразователь ресурсов |
Задает записи в МАНИФЕСТЕ | Преобразователь ManifestResourceTransformer |
Агрегаты Mavens plugin.xml | Плагин Xml Resource Transformer |
Объединяет Пакеты Ресурсов | ResourceBundle AppendingTransformer |
Перемещает имена классов в ресурсах META-INF/services и объединяет их | Сервисыресурстрансформатор |
Добавляет XML-содержимое к XML-ресурсу | Xmlappendingтрансформатор |
Объединяет файлы свойств, имеющие порядковый номер, для разрешения конфликтов | Свойства Трансформатора |
Объединяет файлы конфигурации Apache OpenWebBeans | Трансформатор свойств OpenWebBeans |
Объединяет конфликтующие свойства конфигурации микропрофиля на основе порядкового номера | Трансформатор конфигурации микропрофиля |
Конфигурация плагина Shade для приведенной выше сборки выглядит следующим образом:
maven-shade-plugin shade shade ch.frankel.blog.executablejar.ExecutableJarApplication true
- Цель
shade
по умолчанию привязана к фазеpackage
- Этот преобразователь предназначен для создания файлов манифеста
- Установите запись
Main-Class
- Настройте конечный JAR так, чтобы он был JAR с несколькими выпусками . Это необходимо, когда любой из начальных JAR является JAR с несколькими выпусками
Запуск mvn package
приводит к появлению двух артефактов:
<имя>-<версия>.jar
: автономный исполняемый файл JARоригинал-<имя>-<версия>.jar
: “обычный” JAR без встроенных зависимостей
В примере проекта конечный исполняемый файл по-прежнему работает не так, как ожидалось. Действительно, во время сборки появляется много предупреждений о дублировании ресурсов. Два из них препятствуют правильной работе примерного проекта. Чтобы правильно объединить их, нам нужно взглянуть на их формат:
META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat
: Этот файл Log4J2 содержит предварительно скомпилированные данные плагина Log4J2. Он закодирован в двоичном формате, и ни один из готовых преобразователей не может объединять такие файлы. Тем не менее, случайный поиск показывает, что у кого-то уже была эта проблема, и он выпустил трансформатор для обработки слияния.META-INF/spring.factories
: Эти файлы, специфичные для Spring, имеют формат одного ключа/нескольких значений. Хотя они основаны на тексте, ни один готовый преобразователь не может объединить их правильно. Однако разработчики Spring предоставляют эту возможность (и многое другое) в своем плагине .
Чтобы настроить эти трансформаторы, нам нужно добавить вышеупомянутые библиотеки в качестве зависимостей к плагину Shade:
maven-shade-plugin 3.2.4 shade ch.frankel.blog.executablejar.ExecutableJarApplication true META-INF/spring.factories com.github.edwgiz maven-shade-plugin.log4j2-cachefile-transformer 2.14.0 org.springframework.boot spring-boot-maven-plugin 2.4.1
- Объединить Log4J2
.dat
файлы - Объединить
/META-INF/spring.factories
файлы - Добавьте необходимый код трансформаторов
Добавьте необходимый код трансформаторов Добавьте необходимый код трансформаторов
- Проявляется
- Проявляется
- Файлы, специфичные для Spring Boot т.е.
spring.handlers
,Файлы, специфичные для Spring Boot
т.е. - spring.handlers , Файлы, специфичные для Spring Boot
т.е.
spring.handlers
, - Файлы, специфичные для Spring Boot
Вы можете добавить и настроить дополнительные преобразователи, чтобы исправить оставшиеся предупреждения. В целом, весь процесс требует глубокого понимания каждого вида ресурсов и того, как с ними обращаться.
Плагин Spring Boot
Плагин Spring Boot использует совершенно другой подход. Он не объединяет ресурсы из JARs по отдельности; он добавляет зависимые JARs как они есть внутри uber JAR. Для загрузки классов и ресурсов он предоставляет специальный механизм загрузки классов. Очевидно, что он посвящен проектам Spring Boot.
Настройка плагина Spring Boot проста:
org.springframework.boot spring-boot-maven-plugin 2.4.1 repackage
Давайте проверим структуру конечного JAR:
/ |__ BOOT-INF | |__ classes // 1 | |__ lib // 2 |__ META-INF | |__ MANIFEST.MF |__ org |__ springframework |__ loader // 3
- Скомпилированные классы проекта
- Зависимости JAR
- Класс пружинной загрузки -классы загрузки
Вот выдержка из манифеста для нашего примерного проекта:
Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: ch.frankel.blog.executablejar.ExecutableJarApplication
Как вы можете видеть, основной класс – это класс, специфичный для Spring Boot, в то время как ссылка на “реальный” основной класс содержится в другой записи.
Для получения дополнительной информации о структуре JAR, пожалуйста, ознакомьтесь с справочной документацией .
Вывод
В этом посте мы описали 3 различных способа создания автономных исполняемых файлов JAR:
- Сборка хорошо подходит для простых проектов
- Когда проект начинает усложняться и вам нужно обрабатывать повторяющиеся файлы, используйте Shade
- Наконец, для проектов Spring Boot лучше всего использовать специальный плагин
Полный исходный код для этого поста можно найти на Github в формате Maven.
Чтобы идти дальше:
- Плагин сборки Maven
- Плагин Maven Shade
- maven-затененный-log4j-трансформатор
- Плагин загрузки Maven Spring Boot
- Формат Исполняемого Файла Jar
Первоначально опубликовано на Фанат Java 10 января th 2020
Оригинал: “https://dev.to/nfrankel/creating-self-contained-executable-jars-388d”