Формат исполняемого 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). В следующей таблице описаны эти свойства:
| Ключ | Назначение |
|---|---|
| Список Classpath, разделённый запятыми, например, |
| Используется для разрешения относительных путей в |
| Аргументы по умолчанию для метода main (разделенные пробелами). |
| Имя основного класса для запуска (например, |
| Имя файла свойств (например, |
| Путь к файлу свойств (например, |
| Логический флаг, указывающий, что все свойства должны быть добавлены в системные свойства. По умолчанию значение равно |
При указании в качестве переменных среды или записей манифеста следует использовать следующие имена:
| Ключ | Запись манифеста | Переменная среды |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Плагины сборки автоматически перемещают атрибут 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, рассмотрите следующие альтернативы:
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