Тонкие банки с Spring Boot

Тонкие банки с весенней загрузкой

1. Вступление

В этом руководстве мы рассмотримhow to build a Spring Boot project into a thin JAR file, using the spring-boot-thin-launcher project.

Spring Boot известен своими «толстыми» JAR-развертываниями, где один исполняемый артефакт содержит как код приложения, так и все его зависимости.

Загрузка также широко используется для разработки микросервисов. Иногда это может противоречить подходу «толстый JAR», потому что включение одних и тех же зависимостей снова и снова во многие артефакты может стать важной тратой ресурсов.

2. Предпосылки

Прежде всего, конечно, нам нужен проект Spring Boot. В этой статье мы рассмотрим сборки Maven и сборки Gradle в их наиболее распространенных конфигурациях.

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

2.1. Проекты Maven

В проекте Boot, созданном с помощью Maven, у нас должен быть настроен плагин Spring Boot Maven в файлеpom.xml нашего проекта, его родительском элементе или одном из его предков:


    org.springframework.boot
    spring-boot-maven-plugin

Здесь мы имеем в виду версию плагина2.0.2.RELEASE, самую последнюю на момент написания. Версия зависимостей Spring Boot обычно определяется с помощью спецификации или наследования от родительского POM, как в нашем справочном проекте:


    org.springframework.boot
    spring-boot-starter-parent
    2.0.1.RELEASE
    

2.2. Gradle проекты

В проекте Boot, созданном с помощью Gradle, у нас будет плагин Boot Gradle:

buildscript {
    ext {
        springBootPlugin = 'org.springframework.boot:spring-boot-gradle-plugin'
        springBootVersion = '2.0.1.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("${springBootPlugin}:${springBootVersion}")
    }
}

// elided

apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

springBoot {
    mainClassName = 'org.example.DemoApplication'
}

Обратите внимание, что в этой статье мы будем рассматривать только проекты Boot 2.x и более поздних версий. Thin Launcher также поддерживает более ранние версии, но для этого требуется немного другая конфигурация Gradle, которую мы опускаем для простоты. Пожалуйста, посетите домашнюю страницу проекта для получения более подробной информации.

3. Как создать тонкий JAR?

Spring Boot Thin Launcher - это небольшая библиотека, которая считывает зависимости артефакта из файла, включенного в сам архив, загружает их из репозитория Maven и, наконец, запускает основной класс приложения.

Итак,when we build a project with the library, we get a JAR file with our code, a file enumerating its dependencies, and the main class from the library that performs the above tasks.

Конечно, здесь есть немного больше нюансов, чем наше упрощенное объяснение; мы обсудим некоторые темы более подробно позже в этой статье.

4. Основное использование

Давайте теперь посмотрим, как создать «тонкий» JAR из нашего обычного приложения Spring Boot.

Мы запустим приложение с обычнымjava -jar <my-app-1.0.jar>, с необязательными дополнительными аргументами командной строки, которые управляют тонкой программой запуска. Мы увидим несколько из них в следующих разделах; на домашней странице проекта есть полный список.

4.1. Проекты Maven

В проекте Maven мы должны изменить объявление плагина Boot (см. Раздел 2.1), чтобы включить зависимость от пользовательского «тонкого» макета:


    org.springframework.boot
    spring-boot-maven-plugin
    
        
        
            org.springframework.boot.experimental
            spring-boot-thin-layout
            1.0.11.RELEASE
        
    

launcher будет читать зависимости из файлаpom.xml, который Maven хранит в сгенерированном JAR в каталогеMETA-INF/maven.

Мы выполним сборку как обычно, например, сmvn install.

Если мы хотим иметь возможность создавать как тонкие, так и толстые сборки (например, в проекте с несколькими модулями), мы можем объявить пользовательский макет в выделенном профиле Maven.

4.2. Maven и зависимости:thin.properties

We can also have Maven generate a thin.properties file in addition to pom.xml.. В этом случае файл будет содержать полный список зависимостей, в том числе транзитивных, и программа запуска предпочтет его, а неpom.xml.

Mojo (плагин) для этого -spring-boot-thin-maven-plugin:properties,, и по умолчанию он выводит файлthin.properties вsrc/main/resources/META-INF, но мы можем указать его местоположение с помощью свойстваthin.output:

$ mvn org.springframework.boot.experimental:spring-boot-thin-maven-plugin:properties -Dthin.output=.

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

4.3. Gradle проекты

Вместо этого в проекте Gradle мы добавляем специальный плагин:

buildscript {
    ext {
        //...
        thinPlugin = 'org.springframework.boot.experimental:spring-boot-thin-gradle-plugin'
        thinVersion = '1.0.11.RELEASE'
    }
    //...
    dependencies {
        //...
        classpath("${thinPlugin}:${thinVersion}")
    }
}

//elided

apply plugin: 'maven'
apply plugin: 'org.springframework.boot.experimental.thin-launcher'

Чтобы получить тонкую сборку, мы скажем Gradle выполнить задачуthinJar:

~/projects/example/spring-boot-gradle $ ./gradlew thinJar

4.4. Gradle и зависимости:pom.xml

В примере кода в предыдущем разделе мы объявили плагин Maven в дополнение к Thin Launcher (а также плагины Boot и Dependency Management, которые мы уже видели в разделе Prerequisites).

Это потому, что, как и в случае с Maven, который мы видели ранее, артефакт будет содержать и использовать файлpom.xml, в котором перечислены зависимости приложения. Файлpom.xml создается задачей с именемthinPom, которая является неявной зависимостью от любой задачи jar.

We can customize the generated pom.xml file with a dedicated task. Здесь мы просто воспроизведем то, что тонкий плагин уже делает автоматически:

task createPom {
    def basePath = 'build/resources/main/META-INF/maven'
    doLast {
        pom {
            withXml(dependencyManagement.pomConfigurer)
        }.writeTo("${basePath}/${project.group}/${project.name}/pom.xml")
    }
}

Чтобы использовать наш настраиваемый файлpom.xml, мы добавляем указанную выше задачу к зависимостям задачи jar:

bootJar.dependsOn = [createPom]

4.5. Gradle и зависимости:thin.properties

We can also have Gradle generate a thin.properties file rather than pom.xml,, как мы делали ранее с Maven.

Задача, которая создает файлthin.properties, называетсяthinProperties, и по умолчанию не используется. Мы можем добавить это как зависимость задачи jar:

bootJar.dependsOn = [thinProperties]

5. Хранение зависимостей

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

В частности, Thin Launcher использует инфраструктуру Maven для разрешения зависимостей, поэтому:

  1. он проверяет локальный репозиторий Maven, который по умолчанию находится в~/.m2/repository, но может быть перемещен в другое место;

  2. затем он загружает недостающие зависимости из Maven Central (или любого другого настроенного хранилища);

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

Конечно,the download phase is the slow and error-prone part of the process,, потому что для этого требуется доступ к Maven Central через Интернет или доступ к локальному прокси-серверу, и все мы знаем, насколько эти вещи обычно ненадежны.

К счастью, существуют различные способы развертывания зависимостей вместе с приложениями, например, в предварительно упакованном контейнере для облачного развертывания.

5.1. Запуск приложения для разминки

Самый простой способ кэширования зависимостей - выполнить прогрев приложения в целевой среде. Как мы видели ранее, это приведет к загрузке и кэшированию зависимостей в локальном репозитории Maven. Если мы запустим более одного приложения, репозиторий будет содержать все зависимости без дубликатов.

Поскольку запуск приложения может иметь нежелательные побочные эффекты,we can also perform a “dry run” that only resolves and downloads the dependencies without running any user code:

$ java -Dthin.dryrun=true -jar my-app-1.0.jar

Обратите внимание, что согласно соглашениям Spring Boot, мы можем установить свойство-Dthin.dryrun также с помощью аргумента командной строки–thin.dryrun для приложения или с помощью системного свойстваTHIN_DRYRUN. Любое значение, кромеfalse, укажет тонкой пусковой установке на выполнение пробного запуска.

5.2. Упаковка зависимостей во время сборки

Другой вариант - собрать зависимости во время сборки, не связывая их в JAR. Затем мы можем скопировать их в целевую среду как часть процедуры развертывания.

Как правило, это проще, поскольку нет необходимости запускать приложение в целевой среде. Однако, если мы развертываем несколько приложений, нам придется объединить их зависимости вручную или с помощью скрипта.

Формат, в котором Thin Plugin для Maven и Gradle упаковывает зависимости во время сборки, совпадает с форматом локального репозитория Maven:

root/
    repository/
        com/
        net/
        org/
        ...

Фактически, мы можем указать приложению, использующему Thin Launcher, в любой такой каталог (включая локальный репозиторий Maven) во время выполнения с помощью свойстваthin.root:

$ java -jar my-app-1.0.jar --thin.root=my-app/deps

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

5.3. Упаковка зависимостей с помощью Maven

Чтобы Maven упаковал зависимости для нас, мы используем цельresolve дляspring-boot-thin-maven-plugin.. Мы можем вызывать ее вручную или автоматически в нашемpom.xml:


    org.springframework.boot.experimental
    spring-boot-thin-maven-plugin
    ${thin.version}
    
        
        
        resolve
        
            resolve
        
        false
        
    

После сборки проекта мы найдем каталогtarget/thin/root/ со структурой, которую мы обсуждали в предыдущем разделе.

5.4. Упаковка зависимостей с помощью Gradle

Если вместо этого мы используем Gradle с плагиномthin-launcher, у нас будет доступна задачаthinResolve. Задача сохранит приложение и его зависимости в каталогеbuild/thin/root/, аналогично плагину Maven из предыдущего раздела:

$ gradlew thinResolve

Обратите внимание, что на момент написания в плагинеthin-launcher есть ошибка, которая препятствует сохранению зависимостей, если используетсяthin.properties:https://github.com/dsyer/spring-boot-thin-launcher/issues/53.

6. Выводы и дальнейшее чтение

В этой статье мы рассмотрели, как сделать нашу тонкую банку. Мы также увидели, как использовать инфраструктуру Maven для загрузки и хранения их зависимостей.

Вhomepage тонкой панели запуска есть еще несколько руководств HOW-TO для таких сценариев, как облачное развертывание на Heroku, а также полный список поддерживаемых аргументов командной строки.

Реализацию всех примеров и фрагментов кода Maven можно найти вthe GitHub project - как проект Maven, поэтому его легко импортировать и запускать как есть.

Точно так же все примеры Gradle относятся кthis GitHub project.