Maven и Gradle — системы сборки Java-проектов
Maven и Gradle — инструменты, которые автоматизируют сборку, тестирование и управление зависимостями Java-проектов. Без них невозможно представить разработку на Java в реальной практике.
1. Зачем нужна система сборки
Любой нетривиальный проект состоит из сотен исходных файлов, сторонних библиотек, конфигураций и тестов. Вручную собрать такой проект практически невозможно, поэтому нужна автоматизация.
javac -cp lib1.jar:lib2.jar src/com/example/App.java
java -cp src:lib1.jar:lib2.jar com.example.AppРучная компиляция через javac требует указывать пути ко всем библиотекам. Система сборки решает четыре основные задачи:
- Компиляция — превращение исходников в байт-код.
- Зависимости — скачивание нужных библиотек и их подключение.
- Тесты — запуск модульных тестов и сборка отчётов.
- Упаковка — сборка в JAR, WAR или исполняемый файл.
| Характеристика | Maven | Gradle |
|---|---|---|
| Файл конфигурации | pom.xml (XML) | build.gradle (Groovy/Kotlin) |
| Гибкость | средняя | высокая (полный язык) |
| Скорость | средняя | высокая (инкрементальность) |
| Экосистема | огромная, стандарт де-факто | популярна в Android и новых проектах |
| Порог входа | низкий | средний |
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/src/main/java— исходный код.src/main/resources— ресурсы (конфиги, изображения).src/test/java— код тестов.pom.xml— описание проекта и сборки.
~/.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>- groupId — группа (обычно обратный домен, например com.example).
- artifactId — имя проекта (my-app).
- version — версия артефакта.
- properties — общие настройки, к которым можно обращаться через
${...}. - dependencies — список библиотек с областью видимости scope.
| Scope | Назначение |
|---|---|
| compile | доступна в основном коде (по умолчанию) |
| test | только для тестов |
| provided | предоставляется сервером (например, сервлетный API) |
| runtime | нужна в рантайме, но не при компиляции |
Готовые библиотеки публикуются в репозитории Maven Central — крупнейшем хранилище Java-артефактов. Искать координаты можно на сайте mvnrepository.com.
4. Жизненный цикл Maven
Maven делит сборку на последовательные фазы (phases). Каждая фаза автоматически запускает все предыдущие:
- validate — проверка корректности проекта.
- compile — компиляция исходников.
- test — запуск тестов.
- package — упаковка в JAR/WAR.
- install — копирование артефакта в локальный репозиторий.
- deploy — публикация в удалённый репозиторий.
Основные команды:
mvn clean # очистить каталог target
mvn compile # скомпилировать
mvn test # прогнать тесты
mvn package # собрать jar
mvn install # положить jar в ~/.m2
mvn clean packagemvn clean package — самая частая комбинация: сначала удаляется старый target/, затем проект собирается заново.-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:
- DSL на Groovy или Kotlin вместо XML — код, а не разметка.
- Инкрементальная сборка — перезапускает только изменившиеся части.
- Кэширование и параллельное выполнение задач повышают скорость.
- Гибкость — можно писать логику прямо в build-скрипте.
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()
}- plugins — подключаемые плагины (java, application и другие).
- repositories — откуда брать зависимости (mavenCentral).
- dependencies — блоки
implementationиtestImplementationаналогичны scope в Maven. - application — настройка точки входа приложения.
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 bootRungradle — это установленная на машине система, а ./gradlew — wrapper, который скачивает нужную версию Gradle автоматически. Всегда используй wrapper, чтобы сборка была одинаковой у всех.9. Управление зависимостями
Библиотеки часто тянут за собой другие библиотеки — это переходные зависимости (transitive). Система сборки приносит их автоматически.
Если две библиотеки требуют разные версии одного пакета, возникает конфликт версий. Maven разрешает его по принципу «ближайшая версия», а Gradle — по «последней объявленной».
Посмотреть дерево зависимостей:
mvn dependency:tree
mvn dependency:analyzegradle 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 dependencyUpdates10. Сборка приложений
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.
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 экономит время: зависимости скачиваются только один раз, а не на каждом прогоне пайплайна.
- image — Docker-образ с установленными Java и Maven.
- stages — последовательность этапов пайплайна.
- cache — персистентный кэш между запусками.
- artifacts — сохраняет собранный JAR на сервере GitLab.
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- Активация по свойству:
-Dprofile=ci. - Один профиль может наследовать другой через
<parent>внутри профиля. - Шаблоны по умолчанию держи в основном
pom.xml, а отклонения — в профилях.
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) |
15. Оптимизация времени сборки
Серверы CI платят за каждую минуту сборки. Ходовые ускорения: параллельность, офлайн-режим и постоянный daemon:
mvn -T 4 package # 4 параллельных потока
mvn -o package # офлайн из локального .m2
./gradlew build --parallel --daemon- Строка кэширования
.m2/repositoryв CI не грузит зависимости заново. - Gradle умеет переиспользовать вывод задач из build cache между ветками.
- Профиль
--profile(Gradle) показывает, какие задачи съедают время.
-o навсегда.16. Публикация артефактов и репозитории
Готовую библиотеку нужно выложить в общий репозиторий: локальный Nexus/Artifactory или центральный Maven Central. Без репозитория каждая команда собирает зависимость у себя вручную — бардак и конфликты версий:
./gradlew publish # Gradle
mvn deploy # Maven- Настрой классификацию метаданных: GAV (groupId:artifactId:version), licences, scm — репозитории и IDE их читают.
- Для публикации используют
distributionManagementв Maven иmaven-publishв Gradle. - Версии фиксируют через семвер и теги, генерация SNAPSHOT для разработки — отдельно от релизов.
uniqueVersion/политикой Artifactory: случайное перезаписывание релизной версии ломает сборки у всех потребителей.17. Миграция Maven → Gradle
Gradle читает «из коробки» существующие Maven-проекты, так что большой переезд не обязателен. Постепенная миграция начинается с понимания соответствий:
| Maven | Gradle |
|---|---|
| pom.xml | build.gradle(.kts) + settings.gradle(.kts) |
| dependencies + plugins | plugins {} + dependencies {} |
| mvn install | ./gradlew publishToMavenLocal |
| mvn package | ./gradlew build |
- Первый шаг — не перенос кода, а
gradle initв существующей папке: Gradle сам переведёт pom-структуру. - Управление версиями (BOM, property) переносится в каталог версий
libs.versions.toml. - Сравни вывод:
./gradlew buildдолжен дать те же артефакты, что иmvn package.
18. Пользовательские задачи и плагины
Когда стандартные задачи (`build`, `test`) не решают конкретную потребность, сборку расширяют собственной задачей или плагином:
tasks.register('tagRelease') {
doLast { ... }
}apply plugin: 'java'
tasks.named('test') { useJUnitPlatform() }- Задача с
dependsOnвключает в цепочку «собери → прогони → освободи». - В Maven то же поколение —
exec-maven-pluginили собственный goal через аннотации. - Плагины позволяют переиспользовать эти задачи в других проектах.
19. Диагностика сборок и ошибки
Сборки падают не только из-за кода: пропавшие зависимости, конфликт версий, кеш. Знание типовых ошибок экономит часы изучения логов:
| Симптом | Причина | Решение |
|---|---|---|
Could not resolve ... | нет ключа, недоступен репозиторий | проверь proxy, добавь mirror |
Duplicate class | две версии одной зависимости | exclude лишняя, разрешение конфликта в Gradle |
| Сборка зеленая, тест красный локально | локальный кеш устарел | ./gradlew clean |
| Медленная сборка | нет кеша, много плагинов | configure-on-demand, build cache |
Что дальше
Теперь ты умеешь собирать проекты на Maven и Gradle. Рекомендуемый следующий шаг — изучение JUnit для написания тестов, а затем Spring Boot для построения реальных веб-приложений и их сборки в CI.