Найти в Дзене
Уроки по программированию

Разница между spring-boot:repackage и Maven package

Apache Maven — это широко используемый инструмент для управления зависимостями проекта и его сборки. За последние несколько лет Spring Boot стал весьма популярным фреймворком для создания приложений. Для работы с ним существует также плагин Spring Boot Maven Plugin, который обеспечивает поддержку Spring Boot в Apache Maven. Мы знаем, что если хотим упаковать наше приложение в артефакт JAR или WAR с помощью Maven, мы можем использовать команду mvn package. Однако в составе Spring Boot Maven Plugin также есть цель repackage, которая тоже вызывается через команду mvn. Иногда эти две команды mvn вызывают путаницу. В этом руководстве мы рассмотрим разницу между mvn package и spring-boot:repackage. Прежде всего, мы создадим простое приложение на Spring Boot в качестве примера: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } Чтобы проверить, запущено ли наше приложение и работает ли оно, да
Оглавление

1. Обзор

Apache Maven — это широко используемый инструмент для управления зависимостями проекта и его сборки.

За последние несколько лет Spring Boot стал весьма популярным фреймворком для создания приложений. Для работы с ним существует также плагин Spring Boot Maven Plugin, который обеспечивает поддержку Spring Boot в Apache Maven.

Мы знаем, что если хотим упаковать наше приложение в артефакт JAR или WAR с помощью Maven, мы можем использовать команду mvn package. Однако в составе Spring Boot Maven Plugin также есть цель repackage, которая тоже вызывается через команду mvn.

Иногда эти две команды mvn вызывают путаницу. В этом руководстве мы рассмотрим разницу между mvn package и spring-boot:repackage.

2. Пример приложения на Spring Boot

Прежде всего, мы создадим простое приложение на Spring Boot в качестве примера:

@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

Чтобы проверить, запущено ли наше приложение и работает ли оно, давайте создадим простой REST-эндпоинт:

@RestController
public class DemoRestController {
@GetMapping(value = "/welcome")
public ResponseEntity welcomeEndpoint() {
return ResponseEntity.ok("Welcome to Baeldung Spring Boot Demo!");
}
}

3. Цель package в Maven

Нам потребуется только зависимость spring-boot-starter-web, чтобы собрать наше приложение на Spring Boot:

<artifactId>spring-boot-artifacts-2</artifactId>
<packaging>jar</packaging>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
...

Цель package в Maven возьмёт скомпилированный код и упакует его в распространяемый формат, который в нашем случае представляет собой формат JAR:

$ mvn package
[INFO] Scanning for projects...
[INFO] ------< com.baeldung.spring-boot-modules:spring-boot-artifacts-2 >------
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ spring-boot-artifacts-2 ---
[INFO] Building jar: /home/kent ... /target/spring-boot-artifacts-2.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...

После выполнения команды mvn package мы можем найти собранный JAR-файл spring-boot-artifacts-2.jar в каталоге target. Давайте посмотрим содержимое созданного JAR-файла:

$ jar tf target/spring-boot-artifacts-2.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/baeldung/
com/baeldung/demo/
application.yml
com/baeldung/demo/DemoApplication.class
com/baeldung/demo/DemoRestController.class
META-INF/maven/...

Как видно из вывода выше, JAR-файл, созданный командой mvn package, содержит только ресурсы и скомпилированные Java-классы из исходного кода нашего проекта.

Мы можем использовать этот JAR-файл как зависимость в другом проекте. Однако мы не сможем выполнить этот JAR-файл командой java -jar ИМЯ_ФАЙЛА.jar, даже если это приложение на Spring Boot. Это связано с тем, что исполняемые зависимости в файл не включены. Например, у нас нет встроенного сервлет-контейнера для запуска веб-контекста.

Чтобы запустить наше Spring Boot-приложение с помощью простой команды java -jar, нам нужно собрать так называемый "толстый" JAR-файл (fat JAR). Плагин Spring Boot Maven Plugin может помочь нам в этом.

4. Цель repackage в Spring Boot Maven Plugin

Теперь давайте разберёмся, что делает команда spring-boot:repackage.

4.1. Добавление Spring Boot Maven Plugin

Чтобы выполнить цель repackage, нам нужно добавить Spring Boot Maven Plugin в наш файл pom.xml:

<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

4.2. Выполнение цели spring-boot:repackage

Теперь давайте очистим ранее собранный JAR-файл и попробуем использовать spring-boot:repackage:

$ mvn clean spring-boot:repackage
...
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli) @ spring-boot-artifacts-2 ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
...
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli)
on project spring-boot-artifacts-2: Execution default-cli of goal
org.springframework.boot:spring-boot-maven-plugin:2.3.3.RELEASE:repackage failed: Source file must not be null -> [Help 1]
...

Упс, это не сработало. Это произошло потому, что цель spring-boot:repackage использует существующий JAR- или WAR-архив в качестве исходного файла и перепаковывает все исполняемые зависимости проекта вместе с его классами внутрь итогового артефакта. Таким образом, перепакованный артефакт становится исполняемым через команду java -jar ИМЯ_ФАЙЛА.jar.

Поэтому нам сначала нужно собрать JAR-файл, а уже затем выполнять цель spring-boot:repackage:

$ mvn clean package spring-boot:repackage
...
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ spring-boot-artifacts-2 ---
[INFO] Building jar: /home/kent/.../target/spring-boot-artifacts-2.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli) @ spring-boot-artifacts-2 ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
...

4.3. Содержимое перепакованного JAR-файла

Теперь, если мы посмотрим в каталог target, мы увидим перепакованный JAR-файл и оригинальный JAR-файл:

$ ls -1 target/*jar*
target/spring-boot-artifacts-2.jar
target/spring-boot-artifacts-2.jar.original

Давайте посмотрим содержимое перепакованного JAR-файла:

$ jar tf target/spring-boot-artifacts-2.jar
META-INF/
META-INF/MANIFEST.MF
...
org/springframework/boot/loader/JarLauncher.class
...
BOOT-INF/classes/com/baeldung/demo/
BOOT-INF/classes/application.yml
BOOT-INF/classes/com/baeldung/demo/DemoApplication.class
BOOT-INF/classes/com/baeldung/demo/DemoRestController.class
META-INF/maven/com.baeldung.spring-boot-modules/spring-boot-artifacts-2/pom.xml
META-INF/maven/com.baeldung.spring-boot-modules/spring-boot-artifacts-2/pom.properties
BOOT-INF/lib/
BOOT-INF/lib/spring-boot-starter-web-2.3.3.RELEASE.jar
...
BOOT-INF/lib/spring-boot-starter-tomcat-2.3.3.RELEASE.jar
BOOT-INF/lib/tomcat-embed-core-9.0.37.jar
BOOT-INF/lib/jakarta.el-3.0.3.jar
BOOT-INF/lib/tomcat-embed-websocket-9.0.37.jar
BOOT-INF/lib/spring-web-5.2.8.RELEASE.jar
...
BOOT-INF/lib/httpclient-4.5.12.jar
...

Если мы посмотрим на вывод выше, он значительно длиннее, чем содержимое JAR-файла, созданного командой mvn package.

В перепакованном JAR-файле содержатся не только скомпилированные Java-классы нашего проекта, но и все исполняемые библиотеки, необходимые для запуска нашего приложения на Spring Boot. Например, встроенная библиотека Tomcat упакована в каталог BOOT-INF/lib.

Теперь давайте запустим наше приложение и проверим, работает ли оно:

$ java -jar target/spring-boot-artifacts-2.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
2020-12-22 23:36:32.704 INFO 115154 [main] com.baeldung.demo.DemoApplication : Starting DemoApplication on YK-Arch with PID 11515...
...
2020-12-22 23:36:34.070 INFO 115154 [main] o.s.b.w.embedded.tomcat.TomcatWebServer: Tomcat started on port(s): 8080 (http) ...
2020-12-22 23:36:34.078 INFO 115154 [main] com.baeldung.demo.DemoApplication : Started DemoApplication in 1.766 seconds ...

Наше приложение на Spring Boot запущено и работает. Теперь давайте проверим его, обратившись к нашему эндпоинту /welcome:

$ curl http://localhost:8080/welcome
Welcome to Baeldung Spring Boot Demo!

Отлично! Мы получили ожидаемый ответ. Наше приложение работает корректно.

4.4. Выполнение цели spring-boot:repackage в процессе сборки Maven

Мы можем настроить Spring Boot Maven Plugin в нашем pom.xml, чтобы перепаковка артефакта происходила во время фазы package жизненного цикла Maven. Другими словами, при выполнении команды mvn package цель spring-boot:repackage будет автоматически выполняться.

Настройка довольно проста: нужно просто добавить цель repackage в элемент execution:

<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Теперь давайте снова выполним команду mvn clean package:

$ mvn clean package
...
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default) @ spring-boot-artifacts-2 ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
...

Вывод показывает, что цель repackage была выполнена. Если мы проверим файловую систему, мы увидим, что перепакованный JAR-файл был создан:

$ ls -lh target/*jar*
-rw-r--r-- 1 kent kent 29M Dec 22 23:56 target/spring-boot-artifacts-2.jar
-rw-r--r-- 1 kent kent 3.6K Dec 22 23:56 target/spring-boot-artifacts-2.jar.original

5. Заключение

В этой статье мы рассмотрели разницу между mvn package и spring-boot:repackage.

Также мы узнали, как выполнять spring-boot:repackage во время фазы package жизненного цикла Maven.

Оригинал статьи: https://www.baeldung.com/spring-boot-repackage-vs-mvn-package