Собираем JAR с зависимостями в Maven: пошаговое руководство для новичков

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

«Ваше приложение работает только в IDE? Пора это исправить!»

Если вы когда-нибудь писали на Java, то наверняка сталкивались с ситуацией: вы гордо показываете другу свой новый проект, запускаете его в IntelliJ IDEA или Eclipse — всё работает идеально. Но стоит передать этот проект коллеге или попробовать запустить его через командную строку — и вас встречает ClassNotFoundException или NoClassDefFoundError. Знакомо?

В большинстве учебных курсов и туториалов показывают, как создать JAR файл с помощью Maven, но часто упускают одну важную деталь: куда деваются зависимости?. Обычный JAR-файл содержит только ваш код, а все библиотеки (например, Apache Commons, Jackson, JUnit) остаются за бортом. Именно поэтому возникает вопрос, который задают миллионы Java-разработчиков на Stack Overflow: «How can I create an executable/runnable JAR with dependencies using Maven?»

В этой статье мы разберём несколько проверенных способов собрать исполняемый JAR с зависимостями с помощью Maven. Вы узнаете, как настроить maven-assembly-plugin, maven-shade-plugin и даже как это работает в Spring Boot. Поехали!

Почему обычный JAR не работает?

Давайте проведём простой эксперимент. Представьте, что у вас есть приложение, которое парсит JSON с помощью библиотеки Jackson. Вы создаёте обычный JAR командой mvn package, а затем пытаетесь запустить его:

java -jar my-app.jar

И получаете что-то вроде:

Exception in thread "main" java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ObjectMapper

Почему так происходит? Потому что Maven по умолчанию упаковывает только ваш скомпилированный код (классы из папки src/main/java). Все зависимости, которые вы указали в pom.xml, остаются в локальном репозитории Maven (~/.m2/repository). Когда вы запускаете JAR, JVM не знает, где искать эти библиотеки, и выдаёт ошибку.

Решение — создать fat JAR (толстый JAR) или uber JAR, внутри которого будут не только ваши классы, но и все необходимые зависимости. Именно для этого существуют специальные плагины Maven.

Способ 1: maven-assembly-plugin (просто и надёжно)

Самый популярный и простой способ — использовать maven-assembly-plugin. Он создаёт JAR, в котором все зависимости распакованы и объединены с вашими классами. Давайте настроим его.

Добавьте в ваш pom.xml следующий код в раздел <build><plugins>:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.6.0</version>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
            <manifest>
                <mainClass>com.example.Main</mainClass>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Важно: замените com.example.Main на полное имя вашего класса с методом main.

Теперь соберите проект командой:

mvn clean package

В папке target появятся два файла:

  • my-app.jar — обычный JAR без зависимостей
  • my-app-jar-with-dependencies.jar — наш толстый JAR

Запускаем:

java -jar target/my-app-jar-with-dependencies.jar

Готово! Приложение должно запуститься без ошибок.

Способ 2: maven-shade-plugin (продвинутый, с возможностью переименовывать классы)

Второй популярный вариант — maven-shade-plugin. Он не только объединяет зависимости, но и может решать конфликты версий (если две библиотеки используют один и тот же класс). Особенно это полезно, если вы используете библиотеки, которые сами тянут за собой другие зависимости.

Настройка почти такая же:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.5.1</version>
    <configuration>
        <createDependencyReducedPom>true</createDependencyReducedPom>
        <transformers>
            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                <mainClass>com.example.Main</mainClass>
            </transformer>
        </transformers>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Собираем:

mvn clean package

Результат — target/my-app.jar (shade-плагин заменяет оригинальный JAR). Обратите внимание: плагин создаёт также файл dependency-reduced-pom.xml, в котором зависимости уже не указаны, так как они внутри JAR.

Когда выбирать shade? Если у вас сложный проект с десятками зависимостей, и вы боитесь конфликтов классов (например, Log4j разных версий). Shade умеет «перепаковывать» классы, меняя их пакеты, чтобы избежать коллизий.

Способ 3: Spring Boot Maven Plugin (если вы используете Spring)

Если вы пишете на Spring Boot, то, скорее всего, уже используете spring-boot-maven-plugin. Он автоматически создаёт исполняемый JAR со всеми зависимостями. Ничего дополнительно настраивать не нужно!

Вот минимальная конфигурация pom.xml для Spring Boot приложения:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
</parent>

<build>
    <plugins>
        <plugin>
            <groupId                

Блоги

Рекомендаций по книгам