Maven и Gradle — системы сборки Java-проектов

Maven и Gradle — инструменты, которые автоматизируют сборку, тестирование и управление зависимостями Java-проектов. Без них невозможно представить разработку на Java в реальной практике.

Уровень: средний 19 разделов ⏱ 2–3 часа

1. Зачем нужна система сборки

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

javac -cp lib1.jar:lib2.jar src/com/example/App.java
java -cp src:lib1.jar:lib2.jar com.example.App

Ручная компиляция через javac требует указывать пути ко всем библиотекам. Система сборки решает четыре основные задачи:

ХарактеристикаMavenGradle
Файл конфигурацииpom.xml (XML)build.gradle (Groovy/Kotlin)
Гибкостьсредняявысокая (полный язык)
Скоростьсредняявысокая (инкрементальность)
Экосистемаогромная, стандарт де-фактопопулярна в Android и новых проектах
Порог входанизкийсредний
Обе системы работают с одним и тем же репозиторием библиотек Maven Central, поэтому выбор инструмента не ограничивает набор доступных зависимостей.

2. Что такое Maven

Maven появился в 2004 году как проект Apache. Его главная идея — «конвенции вместо конфигурации» (convention over configuration): вместо того чтобы описывать каждый шаг сборки, достаточно следовать стандартной структуре проекта.

Стандартная структура Maven-проекта:

my-app/
├── pom.xml
└── src/
    ├── main/
    │   ├── java/
    │   │   └── com/example/App.java
    │   └── resources/
    └── test/
        ├── java/
        │   └── com/example/AppTest.java
        └── resources/
Maven все зависимости качает в локальный каталог ~/.m2/repository на диске и переиспользует их для всех проектов.

3. POM.xml

pom.xml (Project Object Model) — сердце Maven. Он описывает координаты проекта, зависимости, плагины и параметры сборки.

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.10.0</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
ScopeНазначение
compileдоступна в основном коде (по умолчанию)
testтолько для тестов
providedпредоставляется сервером (например, сервлетный API)
runtimeнужна в рантайме, но не при компиляции

Готовые библиотеки публикуются в репозитории Maven Central — крупнейшем хранилище Java-артефактов. Искать координаты можно на сайте mvnrepository.com.

4. Жизненный цикл Maven

Maven делит сборку на последовательные фазы (phases). Каждая фаза автоматически запускает все предыдущие:

  1. validate — проверка корректности проекта.
  2. compile — компиляция исходников.
  3. test — запуск тестов.
  4. package — упаковка в JAR/WAR.
  5. install — копирование артефакта в локальный репозиторий.
  6. deploy — публикация в удалённый репозиторий.

Основные команды:

mvn clean        # очистить каталог target
mvn compile      # скомпилировать
mvn test         # прогнать тесты
mvn package      # собрать jar
mvn install      # положить jar в ~/.m2
mvn clean package
mvn clean package — самая частая комбинация: сначала удаляется старый target/, затем проект собирается заново.
Если сборка падает в фазе test, значит упал хотя бы один тест. Для обхода можно использовать -DskipTests, но это плохая практика — всегда сначала чини тесты.

5. Практика Maven

Создание проекта через archetype

Archetype — это шаблон проекта. Быстрее всего создать заготовку командой:

mvn archetype:generate \
  -DgroupId=com.example \
  -DartifactId=my-app \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false

Будет создан каталог my-app со стандартной структурой и готовым pom.xml.

Полный pom.xml для Java + JUnit

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>calculator</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.10.0</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.2.2</version>
      </plugin>
    </plugins>
  </build>
</project>
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class CalculatorTest {

    @Test
    void testAdd() {
        assertEquals(4, Calculator.add(2, 2));
    }
}

Запускай тесты командой mvn test и смотри отчёт в target/surefire-reports.

6. Что такое Gradle

Gradle — современная система сборки, появившаяся в 2007 году как ответ на ограничения Maven. Вместо заранее заданных жизненных циклов она использует задачи (tasks) и полноценный язык программирования для описания сборки.

Отличия от Maven:

Gradle — стандарт для Android-разработки и всё чаще используется в новых проектах на Java, включая крупные фреймворки.

7. build.gradle

Основной файл Gradle называется build.gradle для Groovy DSL или build.gradle.kts для Kotlin DSL. Приведём пример для Java-проекта:

plugins {
    id 'java'
    id 'application'
}

group = 'com.example'
version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    implementation 'com.google.code.gson:gson:2.10.1'
}

application {
    mainClass = 'com.example.App'
}

test {
    useJUnitPlatform()
}
Плагин java автоматически добавляет стандартные задачи сборки, а application — возможность запуска через ./gradlew run.

8. Задачи Gradle

В Gradle всё управляется задачами. Основные:

gradle build      # полная сборка (компиляция + тесты + упаковка)
gradle clean      # очистка каталога build
gradle test       # запуск тестов
gradle run        # запуск приложения (нужен плагин application)
gradle wrapper    # генерация wrapper
gradle tasks      # список всех задач

Для Spring Boot используется отдельная задача запуска:

./gradlew bootRun
Не путай: gradle — это установленная на машине система, а ./gradlew — wrapper, который скачивает нужную версию Gradle автоматически. Всегда используй wrapper, чтобы сборка была одинаковой у всех.

9. Управление зависимостями

Библиотеки часто тянут за собой другие библиотеки — это переходные зависимости (transitive). Система сборки приносит их автоматически.

Если две библиотеки требуют разные версии одного пакета, возникает конфликт версий. Maven разрешает его по принципу «ближайшая версия», а Gradle — по «последней объявленной».

Посмотреть дерево зависимостей:

mvn dependency:tree
mvn dependency:analyze
gradle dependencies

Чтобы исключить лишнюю переходную зависимость в Maven, используется exclusions:

<dependency>
  <groupId>com.example</groupId>
  <artifactId>some-lib</artifactId>
  <version>1.0.0</version>
  <exclusions>
    <exclusion>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
    </exclusion>
  </exclusions>
</dependency>

В Gradle то же самое выглядит так:

implementation('com.example:some-lib:1.0.0') {
    exclude group: 'org.slf4j', module: 'slf4j-simple'
}

Обновлять версии удобно подключаемыми плагинами: в Maven это плагин versions-maven-plugin, в Gradle — com.github.ben-manes.versions.

mvn versions:display-dependency-updates
./gradlew dependencyUpdates

10. Сборка приложений

Spring Boot

Spring Boot для обоих инструментов поставляется с готовым wrapper. Удобно пользоваться командами:

# Maven
./mvnw clean package
./mvnw spring-boot:run
java -jar target/app-1.0.0.jar

# Gradle
./gradlew clean build
./gradlew bootRun
java -jar build/libs/app-1.0.0.jar

Команда package создаёт fat-jar — JAR, внутри которого лежит всё приложение вместе с зависимостями. Такой файл можно просто запустить через java -jar.

Fat-jar может весить десятки мегабайт, потому что содержит все библиотеки проекта. Это нормально для сервисных приложений.

Multi-module проекты

Большие системы разбивают на несколько модулей. В Maven используется родительский POM с разделом <modules>:

<modules>
  <module>common</module>
  <module>api</module>
  <module>web</module>
</modules>

В Gradle аналогичная структура задаётся в файле settings.gradle:

rootProject.name = 'my-project'
include 'common', 'api', 'web'

Сборка выполняется один раз на верхнем уровне и собирает все модули в правильном порядке.

11. CI и сборка

Систему сборки обязательно используют в непрерывной интеграции (CI). Единая команда mvn package или ./gradlew build позволяет любому серверу собрать проект без ручных шагов.

Пример конфигурации GitLab CI для Maven-проекта (.gitlab-ci.yml):

image: maven:3.9-eclipse-temurin-17

stages:
  - build
  - test
  - package

cache:
  paths:
    - .m2/repository

build:
  stage: build
  script:
    - mvn compile

test:
  stage: test
  script:
    - mvn test

package:
  stage: package
  script:
    - mvn package
  artifacts:
    paths:
      - target/*.jar

Кэширование каталога .m2/repository экономит время: зависимости скачиваются только один раз, а не на каждом прогоне пайплайна.

Аналогично настраивается GitHub Actions: шаги actions/setup-java и mvn package покрывают тот же сценарий.

12. Профили Maven

Профили переключают конфигурацию сборки по окружению: локальная разработка, staging и релизный вариант могут отличаться настройками без правки кода:

<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <skipTests>true</skipTests>
    </properties>
  </profile>
</profiles>
mvn package -Pdev          # без тестов, для локального запуска
mvn package -Pprod

13. Многомодульные проекты

Крупные системы делятся на модули: ядро, API, приложение. Maven перечисляет их в parent-POM:

<modules>
  <module>core</module>
  <module>web</module>
</modules>

В Gradle модули указываются в settings.gradle.kts, а связи — зависимостями проекта:

rootProject.name = "my-app"
include("core", "web")
dependencies {
    implementation(project(":core"))
}
Модульность окупается, когда сборка становится по-настоящему параллельной: общие зависимости поднимаются наверх, а изменённый модуль пересобирается один.

14. Версионирование артефактов и релизы

Версия артефакта — контракт для потребителей: MAJOR ломает совместимость, MINOR добавляет функциональность, PATCH чинит ошибки. Автоинкремент берёт на себя плагин:

ПодходИнструмент
Версия вручную в POMесть в любой сборке
Автоматический релизmaven-release-plugin
Версия из Git-тэгаCI + git describe
Без явной версииnebula.release (Gradle)
Не коммить вручную новую версию после релиза: плагины делают «переход» версии сами, и ручное вмешательство в истории даёт рассинхрон с тегами Git.

15. Оптимизация времени сборки

Серверы CI платят за каждую минуту сборки. Ходовые ускорения: параллельность, офлайн-режим и постоянный daemon:

mvn -T 4 package         # 4 параллельных потока
mvn -o package           # офлайн из локального .m2
./gradlew build --parallel --daemon
Офлайн-режим работает только при полном кэше: один недостающий плагин — и сборка падает. Оставляй ошибки на месте, а не молча включай -o навсегда.

16. Публикация артефактов и репозитории

Готовую библиотеку нужно выложить в общий репозиторий: локальный Nexus/Artifactory или центральный Maven Central. Без репозитория каждая команда собирает зависимость у себя вручную — бардак и конфликты версий:

./gradlew publish   # Gradle
mvn deploy          # Maven
Кладя snapshot и релиз в один репозиторий, следи за uniqueVersion/политикой Artifactory: случайное перезаписывание релизной версии ломает сборки у всех потребителей.

17. Миграция Maven → Gradle

Gradle читает «из коробки» существующие Maven-проекты, так что большой переезд не обязателен. Постепенная миграция начинается с понимания соответствий:

MavenGradle
pom.xmlbuild.gradle(.kts) + settings.gradle(.kts)
dependencies + pluginsplugins {} + dependencies {}
mvn install./gradlew publishToMavenLocal
mvn package./gradlew build
Не мигрируй «разом» всё: сперва один модуль на Gradle, затем второй. Смешанный режим (Maven качает из локального репозитория Gradle) работает, если обе системы смотрят в один local-репозиторий.

18. Пользовательские задачи и плагины

Когда стандартные задачи (`build`, `test`) не решают конкретную потребность, сборку расширяют собственной задачей или плагином:

tasks.register('tagRelease') {
    doLast { ... }
}
apply plugin: 'java'
tasks.named('test') { useJUnitPlatform() }
Пиши задачи маленькими и детерминированными: кеш Gradle ломается, если задача меняет состояние вне своих inputs/outputs. Объяви все входы явно.

19. Диагностика сборок и ошибки

Сборки падают не только из-за кода: пропавшие зависимости, конфликт версий, кеш. Знание типовых ошибок экономит часы изучения логов:

СимптомПричинаРешение
Could not resolve ...нет ключа, недоступен репозиторийпроверь proxy, добавь mirror
Duplicate classдве версии одной зависимостиexclude лишняя, разрешение конфликта в Gradle
Сборка зеленая, тест красный локальнолокальный кеш устарел./gradlew clean
Медленная сборканет кеша, много плагиновconfigure-on-demand, build cache
Никогда не «лечи» падение сборки отключением теста навсегда. Понимай причину, иначе пропустишь падение, которое через месяц укусит в проде.

Что дальше

Теперь ты умеешь собирать проекты на Maven и Gradle. Рекомендуемый следующий шаг — изучение JUnit для написания тестов, а затем Spring Boot для построения реальных веб-приложений и их сборки в CI.

← Вернуться к курсам