Spec-Zone.ru › Spring Boot

Формат исполняемого JAR-архива

Модули spring-boot-loader позволяют Spring Boot поддерживать исполняемые JAR- и WAR-файлы. Если вы используете плагин Maven или Gradle, исполняемые JAR-файлы генерируются автоматически, и вам, как правило, не нужно знать подробности их работы.

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

1. Вложенные JAR-архивы

Java не предоставляет стандартного способа загрузки вложенных JAR-файлов (то есть JAR-файлов, которые сами содержатся внутри JAR-файла). Это может быть проблематично, если вам нужно распространить автономное приложение, которое может запускаться из командной строки без распаковки.

Для решения этой проблемы многие разработчики используют «затенённые» JAR-файлы. Затенённый JAR-архив упаковывает все классы из всех JAR-файлов в один «сборный JAR-файл». Проблема с затенёнными JAR-файлами заключается в том, что становится трудно определить, какие библиотеки фактически используются в вашем приложении. Это также может быть проблематично, если в нескольких JAR-файлах используется одно и то же имя файла (но с разным содержимым). Spring Boot использует другой подход и позволяет вкладывать JAR-файлы непосредственно.

1.1. Структура исполняемого JAR-файла

JAR-файлы, совместимые с загрузчиком Spring Boot, должны быть структурированы следующим образом:

example.jar
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-BOOT-INF
    +-classes
    |  +-mycompany
    |     +-project
    |        +-YourClasses.class
    +-lib
       +-dependency1.jar
       +-dependency2.jar

Классы приложения должны быть размещены во вложенной BOOT-INF/classes директории. Зависимости должны быть размещены во вложенной BOOT-INF/lib директории.

1.2. Структура исполняемого WAR-файла

WAR-файлы, совместимые с загрузчиком Spring Boot, должны быть структурированы следующим образом:

example.war
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-WEB-INF
    +-classes
    |  +-com
    |     +-mycompany
    |        +-project
    |           +-YourClasses.class
    +-lib
    |  +-dependency1.jar
    |  +-dependency2.jar
    +-lib-provided
       +-servlet-api.jar
       +-dependency3.jar

Зависимости должны быть размещены во вложенной WEB-INF/lib директории. Любые зависимости, необходимые при запуске встроенного приложения, но не необходимые при развертывании в традиционной веб-контейнере, должны быть размещены в WEB-INF/lib-provided.

1.3. Файлы индексов

Совместимые с загрузчиком Spring Boot JAR- и WAR-архивы могут содержать дополнительные файлы индексов в каталоге BOOT-INF/. Файл classpath.idx может быть предоставлен для JAR- и WAR-архивов, и он определяет порядок добавления JAR-файлов в путь класса. Файл layers.idx может использоваться только для JAR-архивов и позволяет разделить JAR-архив на логические слои для создания образов Docker/OCI.

Файлы индексов следуют синтаксису, совместимому с YAML, чтобы их можно было легко анализировать сторонними инструментами. Однако эти файлы не интерпретируются как YAML и должны быть записаны в точности в описанных ниже форматах для корректного использования.

1.4. Индекс пути класса

Файл индекса пути класса может быть предоставлен в BOOT-INF/classpath.idx. Он содержит список имён JAR-файлов (включая директорию) в порядке их добавления в путь класса. Каждая строка должна начинаться с тире и пробела ("-·"), а имена должны быть в двойных кавычках.

Например, для следующего JAR-файла:

example.jar
 |
 +-META-INF
 |  +-...
 +-BOOT-INF
    +-classes
    |  +...
    +-lib
       +-dependency1.jar
       +-dependency2.jar

Файл индекса будет выглядеть так:

- "BOOT-INF/lib/dependency2.jar"
- "BOOT-INF/lib/dependency1.jar"

1.5. Индекс слоёв

Файл индекса слоёв может быть предоставлен в BOOT-INF/layers.idx. Он содержит список слоёв и части JAR-файла, которые должны быть включены в них. Слои записываются в порядке их добавления в образ Docker/OCI. Имена слоёв записываются как строки в кавычках, префикс которых — тире и пробел ("-·"), а суффикс — двоеточие (":"). Содержимое слоёв — это имена файлов или каталогов, записанные в кавычках с префиксом двойной пробел тире пробел ("··-·"). Имя каталога заканчивается на /, имя файла — нет. Использование имени каталога означает, что все файлы внутри этого каталога находятся в одном слое.

Типичным примером индекса слоёв является:

- "dependencies":
  - "BOOT-INF/lib/dependency1.jar"
  - "BOOT-INF/lib/dependency2.jar"
- "application":
  - "BOOT-INF/classes/"
  - "META-INF/"

2. Класс «JarFile» Spring Boot

Основной класс, используемый для поддержки загрузки вложенных jar-файлов, это org.springframework.boot.loader.jar.JarFile. Он позволяет загружать содержимое jar-файла из стандартного jar-файла или из данных вложенных дочерних jar-файлов. При первой загрузке местоположение каждого JarEntry отображается на физический смещение файла внешнего jar-файла, как показано в следующем примере:

myapp.jar
+-------------------+-------------------------+
| /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar |
|+-----------------+||+-----------+----------+|
||     A.class      |||  B.class  |  C.class ||
|+-----------------+||+-----------+----------+|
+-------------------+-------------------------+
 ^                    ^           ^
 0063                 3452        3980

Представленный пример показывает, как A.class можно найти в /BOOT-INF/classes в myapp.jar в позиции 0063. B.class из вложенного jar-файла фактически находится в myapp.jar в позиции 3452, а C.class — в позиции 3980.

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

2.1. Совместимость с стандартным классом Java «JarFile»

Spring Boot Loader стремится сохранить совместимость с существующим кодом и библиотеками. org.springframework.boot.loader.jar.JarFile расширяется от java.util.jar.JarFile и должен работать как замена без изменений. Метод getURL() возвращает URL, который открывает соединение, совместимое с java.net.JarURLConnection и может использоваться с URLClassLoader Java.

3. Запуск исполняемых jar-файлов

Класс org.springframework.boot.loader.Launcher — это специальный класс-загрузчик, который используется в качестве основной точки входа для исполняемого jar-файла. Это фактически Main-Class в вашем jar-файле, и он используется для настройки соответствующего URLClassLoader и, в конечном итоге, вызова вашего метода main().

Существует три подкласса загрузчика (JarLauncher, WarLauncher, и PropertiesLauncher). Их цель — загружать ресурсы (файлы .class и так далее) из вложенных jar-файлов или war-файлов в каталогах (в отличие от тех, что явно указаны в классе). В случае JarLauncher и WarLauncher вложенные пути фиксированы. JarLauncher ищет в BOOT-INF/lib/, а WarLauncher ищет в WEB-INF/lib/ и WEB-INF/lib-provided/. Вы можете добавить дополнительные jar-файлы в эти места, если нужно. PropertiesLauncher по умолчанию ищет в BOOT-INF/lib/ в вашем прикладном архиве. Вы можете добавить дополнительные места, задав переменную среды, называемую LOADER_PATH или loader.path в loader.properties (это список каталогов, архивов или каталогов внутри архивов, разделённый запятыми).

3.1. Файл манифеста загрузчика

Вам необходимо указать соответствующий Launcher в качестве атрибута Main-Class в META-INF/MANIFEST.MF. Фактический класс, который вы хотите запустить (то есть класс, содержащий метод main ), должен быть указан в атрибуте Start-Class.

Следующий пример показывает типичный MANIFEST.MF для исполняемого jar-файла:

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.mycompany.project.MyApplication

Для war-файла это будет выглядеть следующим образом:

Main-Class: org.springframework.boot.loader.WarLauncher
Start-Class: com.mycompany.project.MyApplication
Вам не нужно указывать Class-Path записи в вашем файле манифеста. Путь к классам определяется по вложенным jar-файлам.

4. Функции PropertiesLauncher

PropertiesLauncher обладает несколькими специальными функциями, которые можно включить с помощью внешних свойств (системные свойства, переменные среды, записи манифеста или loader.properties). В следующей таблице описаны эти свойства:

Ключ Назначение

loader.path

Список Classpath, разделённый запятыми, например, lib,${HOME}/app/lib. Более ранние записи имеют приоритет, как и обычный -classpath в командной строке javac.

loader.home

Используется для разрешения относительных путей в loader.path. Например, если задано loader.path=lib, то ${loader.home}/lib — это местоположение classpath (вместе со всеми файлами jar в этом каталоге). Это свойство также используется для поиска файла loader.properties, как в следующем примере /opt/app. По умолчанию значение равно ${user.dir}.

loader.args

Аргументы по умолчанию для метода main (разделенные пробелами).

loader.main

Имя основного класса для запуска (например, com.app.Application).

loader.config.name

Имя файла свойств (например, launcher). По умолчанию значение равно loader.

loader.config.location

Путь к файлу свойств (например, classpath:loader.properties). По умолчанию значение равно loader.properties.

loader.system

Логический флаг, указывающий, что все свойства должны быть добавлены в системные свойства. По умолчанию значение равно false.

При указании в качестве переменных среды или записей манифеста следует использовать следующие имена:

Ключ Запись манифеста Переменная среды

loader.path

Loader-Path

LOADER_PATH

loader.home

Loader-Home

LOADER_HOME

loader.args

Loader-Args

LOADER_ARGS

loader.main

Start-Class

LOADER_MAIN

loader.config.location

Loader-Config-Location

LOADER_CONFIG_LOCATION

loader.system

Loader-System

LOADER_SYSTEM

Плагины сборки автоматически перемещают атрибут Main-Class в Start-Class при построении жирного jar-файла. Если вы используете это, укажите имя запускаемого класса, используя атрибут Main-Class, и опустите Start-Class.

Следующие правила применяются к работе с PropertiesLauncher:

  • loader.properties ищется в loader.home, затем в корне classpath и, наконец, в classpath:/BOOT-INF/classes. Используется первое местоположение, где файл с таким именем существует.

  • loader.home — это местоположение каталога дополнительного файла свойств (переопределяющего значение по умолчанию), только когда loader.config.location не указано.

  • loader.path может содержать каталоги (которые рекурсивно сканируются на наличие файлов jar и zip), пути к архивам, каталог внутри архива, который сканируется на наличие файлов jar (например, dependencies.jar!/lib или шаблоны подстановок (для стандартного поведения JVM). Пути к архивам могут быть относительными к loader.home или где угодно в файловой системе с префиксом jar:file:.

  • loader.path (если пусто) по умолчанию равно BOOT-INF/lib (что означает локальный каталог или вложенный, если запуск из архива). Благодаря этому PropertiesLauncher ведет себя так же, как и JarLauncher при отсутствии дополнительной конфигурации.

  • loader.path не может использоваться для конфигурирования расположения loader.properties (classpath, используемый для поиска последнего, — это classpath JVM при запуске PropertiesLauncher).

  • Замена плейсхолдеров выполняется из системных и переменных среды, а также из самого файла свойств для всех значений перед использованием.

  • Порядок поиска свойств (где целесообразно искать в нескольких местах) — переменные среды, системные свойства, loader.properties, манифест развёрнутого архива и манифест архива.

5. Ограничения исполняемого Jar-файла

При работе с приложением, упакованным с помощью Spring Boot Loader, необходимо учитывать следующие ограничения:

  • Сжатие записей Zip: ZipEntry для вложенного jar-файла необходимо сохранить, используя метод ZipEntry.STORED. Это необходимо для того, чтобы можно было напрямую перейти к определённому содержимому внутри вложенного jar-файла. Содержимое самого вложенного jar-файла всё ещё может быть сжато, как и любые другие записи во внешнем jar-файле.

  • System classLoader: При запуске приложения следует использовать Thread.getContextClassLoader() при загрузке классов (большинство библиотек и фреймворков делают это по умолчанию). Попытка загрузить классы вложенного jar-файла с помощью ClassLoader.getSystemClassLoader() терпит неудачу. java.util.Logging всегда использует системный classloader. По этой причине следует рассмотреть другой механизм ведения журнала.

6. Альтернативные решения для одного jar-файла

Если указанные ограничения означают, что вы не можете использовать Spring Boot Loader, рассмотрите следующие альтернативы:

  • Плагин Maven Shade

  • JarClassLoader

  • OneJar

  • Плагин Gradle Shadow

Copyright © 2012-2023 VMware, Inc.
Licensed under the Apache License, Version 2.0.
https://docs.spring.io/spring-boot/docs/3.1.3/reference/html/executable-jar.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API