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

Создание автономных исполняемых файлов JAR

Когда ваше приложение выходит за рамки дюжины строк кода, вам, вероятно, следует разделить код на m… Помечен как java, jar, maven, spring boot.

Когда ваше приложение выходит за рамки дюжины строк кода, вам, вероятно, следует разделить код на несколько классов. На данный момент вопрос заключается в том, как их распределить. В 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
  1. Основной класс имеет/| статический main(String… аргументы) способ

Обработка пути к классу

Большинство приложений зависят от существующего кода. Java предоставляет концепцию classpath . Путь к классу – это список элементов пути, которые среда выполнения будет просматривать, чтобы найти зависимый код. Когда запускаются классы Java , вы определяете путь к классу с помощью параметра командной строки -cp :

java -cp lib/one.jar;lib/two.jar;/var/lib/three.jar path.to.MainClass

Среда выполнения Java создает путь к классу путем объединения всех классов из всех ссылочных JAR и добавления основного класса.

Новые проблемы возникают при распределении банок, которые зависят от других банок:

  1. Вам нужно определить одни и те же библиотеки в одной и той же версии
  2. Что еще более важно, аргумент -cp не работает с JARs . Чтобы ссылаться на другие JAR, путь к классу должен быть задан в манифесте JAR с помощью атрибута ClassPath :

  3. По этой причине вам необходимо поместить банки в одно и то же местоположение, относительное или абсолютное, в целевой файловой системе в соответствии с манифестом. Это подразумевает, что сначала нужно открыть JAR и прочитать манифест.

Одним из способов решения этих проблем является создание уникального модуля развертывания, который содержит классы из всех JAR и который может быть распространен как один артефакт. Существует несколько вариантов создания таких банок:

  • Плагин для сборки
  • Плагин Shade
  • Плагин Spring Boot (для проектов Spring Boot)

Плагин сборки Apache

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

Плагин сборки Apache Maven

Одним из правил проектирования Maven является создание одного артефакта для каждого проекта. Есть исключения например Артефакты Javadocs и исходные артефакты, но в целом, если вам нужно несколько артефактов, вам нужно создать один проект для каждого артефакта. Идея плагина Assembly заключается в том, чтобы обойти это правило.

Плагин сборки опирается на определенный assembly.xml |/конфигурационный файл. Это позволяет вам выбирать, какие файлы будут включены в артефакт. Обратите внимание, что конечный артефакт не обязательно должен быть JAR: файл конфигурации позволяет вам выбирать между доступными форматами например, zip, war и т.д.

Плагин управляет распространенными вариантами использования, предоставляя предварительно определенные сборки. Среди них – распространение автономных банок. Конфигурация выглядит следующим образом:


  maven-assembly-plugin
  
    
      jar-with-dependencies                            
    
    
      
        ch.frankel.blog.executablejar.ExecutableJarApplication 
      
    
  
  
    
      
        single                                                           
      
      package                                                          
    
  

  1. Обратитесь к предварительно определенной автономной конфигурации JAR
  2. Установите основной класс для выполнения
  3. Выполните единственную цель
  4. Привяжите цель к package phase т.е. после исходного JAR был построен

Запуск mvn package приводит к появлению двух артефактов:

  1. <имя>-<версия>.jar
  2. <имя>-<версия>-с- 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, включая его зависимости, и затенять – то есть переименовывать – пакеты некоторых зависимостей.

Плагин Apache Maven Shade

Плагин основан на концепции 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 
            
          
        
      
    
  

  1. Цель shade по умолчанию привязана к фазе package
  2. Этот преобразователь предназначен для создания файлов манифеста
  3. Установите запись Main-Class
  4. Настройте конечный JAR так, чтобы он был JAR с несколькими выпусками . Это необходимо, когда любой из начальных JAR является JAR с несколькими выпусками

Запуск mvn package приводит к появлению двух артефактов:

  1. <имя>-<версия>.jar : автономный исполняемый файл JAR
  2. оригинал-<имя>-<версия>.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
    
  

  1. Объединить Log4J2 .dat файлы
  2. Объединить /META-INF/spring.factories файлы
  3. Добавьте необходимый код трансформаторов

Добавьте необходимый код трансформаторов Добавьте необходимый код трансформаторов

  • Проявляется
  • Проявляется
  • Файлы, специфичные для 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
  1. Скомпилированные классы проекта
  2. Зависимости JAR
  3. Класс пружинной загрузки -классы загрузки

Вот выдержка из манифеста для нашего примерного проекта:

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: ch.frankel.blog.executablejar.ExecutableJarApplication

Как вы можете видеть, основной класс – это класс, специфичный для Spring Boot, в то время как ссылка на “реальный” основной класс содержится в другой записи.

Для получения дополнительной информации о структуре JAR, пожалуйста, ознакомьтесь с справочной документацией .

Вывод

В этом посте мы описали 3 различных способа создания автономных исполняемых файлов JAR:

  1. Сборка хорошо подходит для простых проектов
  2. Когда проект начинает усложняться и вам нужно обрабатывать повторяющиеся файлы, используйте Shade
  3. Наконец, для проектов Spring Boot лучше всего использовать специальный плагин

Полный исходный код для этого поста можно найти на Github в формате Maven.

Чтобы идти дальше:

Первоначально опубликовано на Фанат Java 10 января th 2020

Оригинал: “https://dev.to/nfrankel/creating-self-contained-executable-jars-388d”