Перейти к основному содержимому

Структура и сборки Java-проектов

Разработчику Архитектору

Структуры проекта

Что такое пакет?

Пакетом (пространством имен) в Java называется структура вложенных по какому-то признаку папок с размещенными в них классами (интерфейсами, перечислениями, аннотациями), необходимыми проекту.

В Java используется пакетная структура – способ организации классов в папках, который помогает избежать конфликтов имён и группировать код логически. Это обязательная структура. Чтобы система понимала, что означает команда, указанная программистом в исходном коде в виде слова, нужно сообщить системе об использовании пакета, который содержит код-содержание этого слова:

"Дружище, налей мне молока!"
"А где мне взять молоко?".

Мы, люди, многие элементарные вещи уже изначально автоматизировали в себе и понимаем подсознательно. Мы прекрасно знаем, что молоко в холодильнике. Но программе нужно показать, что нам понадобится холодильник. Поэтому общение с программой будет как-то так:

"Дружище, запомни – вот холодильник. Там есть молоко".
"Дружище, налей мне молока!"
"Ок, вижу, молоко в холодильнике. Сейчас сделаю".

Вот поэтому код библиотеки или проекта формируется в составе пакета (package).

Практический эффект чувствуется сразу — как только проект перестаёт быть "одним файлом", правильная пакетная структура экономит часы на навигации, поиске и согласовании изменений в команде.

Пакетная структура решает две задачи: предотвращает конфликты имён между классами из разных источников и группирует связанный код для удобства навигации и поддержки.

Play ITЗагрузка интерактивного демо…

Пакет — это механизм организации классов и интерфейсов в пространстве имен (namespace), который позволяет структурировать код, предотвращать конфликты имен и управлять доступом к компонентам программы. В Java пакеты соответствуют структуре директорий на диске.


Структура пакетов

Каждый Java-файл начинается с директивы package, указывающей полный путь к пакету.

package com.example.myapp.utils;

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

Физическая структура каталогов соответствует логическому имени пакета. Для класса с объявлением package com.example.app; компилятор ожидает размещение исходного файла по пути com/example/app/ClassName.java. Обратный слеш в имени пакета заменяется на разделитель каталогов операционной системы.

Имена пакетов строятся по обратному порядку домена организации, и хранятся в строгой структуре:

  • com/mycompany/project/Main.java – физический путь;
  • com.mycompany.project – путь в коде.

com – корень пакета. Только строчные буквы. Это префикс домена, обычно обратный интернет-домен компании или проекта. Нужен для уникальности пакетов в мире. Можно указывать своё, но рекомендуется использовать стандарт:

  • com – для коммерческих проектов;
  • org – для открытых проектов;
  • net, io, ru – тоже допустимы.

mycompany – название компании или автора, допустим, имя (если проект личный). Желательно делать уникальным. Только строчные буквы, несмотря на то что это будет имя или название – допустим, не Timur, а timur.

project – название проекта или модуля, библиотеки, подсистемы. Допустим, calculator, utils или как-то так. Только строчные буквы, название может быть любым.

Такой подход гарантирует глобальную уникальность имён пакетов. Два разработчика из разных компаний могут создать класс User, но их полные имена com.companyA.app.User и org.companyB.tool.User никогда не пересекутся.


Система сборки в Java

Современные Java-проекты используют системы сборки Maven и Gradle. Это инструменты, которые автоматизируют компиляцию кода, управление зависимостями (библиотеками), запуск тестов, создание готовых приложений и развёртывание (деплой).

По сути, сборка — это повторяемый сценарий "собери одинаково у каждого разработчика и на CI". Именно повторяемость отличает инженерный процесс от ручных запусков команд.

Система сборки в Java представляет собой автоматизированный процесс преобразования исходного кода и ресурсов в исполняемый продукт, готовый к запуску или распространению. Этот процесс объединяет компиляцию файлов, проверку зависимостей, управление версиями и упаковку результатов в единый формат.

В экосистеме Java стандартом де-факто для управления сборкой является инструмент Maven. Он работает на основе декларативного подхода: разработчик описывает проект и его потребности в файле конфигурации pom.xml, а система сама выполняет необходимые шаги для получения результата.

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

  1. Очистка (Clean) — удаляет target/ (см. mvn clean в блоке CLI ниже).

  2. Сборка (Compile). На этой стадии компилятор Java обрабатывает исходные файлы .java, расположенные в папке src/main/java. Инструмент проверяет правильность синтаксиса, соответствие типов данных и наличие всех объявленных классов и методов. Если ошибок нет, генерируются файлы .class в папке target/classes. Эти файлы содержат байт-код, который является платформенно-независимым представлением программы. Компиляция также включает обработку аннотаций и генерацию вспомогательных файлов, если они требуются фреймворками.

  3. Тестирование (Test). Система копирует скомпилированные классы в специальную директорию target/test-classes и запускает набор тестов, находящихся в папке src/test/java. Тесты проверяют логику работы отдельных методов и классов. Если хотя бы один тест падает, сборка останавливается, и процесс завершается ошибкой. Это предотвращает выпуск продуктов с известными дефектами. Результаты тестирования сохраняются в виде отчетов в формате XML или HTML, которые можно проанализировать позже.

  4. Упаковка (Package). Скомпилированный код и ресурсы объединяются в архивный файл. Для стандартных приложений это JAR-файл (Java ARchive), содержащий классы и метаданные. Для веб-приложений создается WAR-файл (Web ARchive), включающий HTML, CSS, JavaScript и конфигурационные файлы. Упакованный артефакт становится готовым к развертыванию продуктом. В процессе упаковки также может происходить проверка целостности файлов и их подписывание цифровыми сертификатами.

  5. Установка (Install). Результат предыдущей стадии помещается в локальный репозиторий пользователя. Обычно это папка .m2/repository в домашней директории операционной системы. Установка делает библиотеку доступной для других проектов, работающих на этом компьютере. Если другой проект ссылается на эту библиотеку по групповому идентификатору, имени и версии, он найдет её в локальном хранилище без необходимости скачивания из сети.

  6. Развертывание (Deploy). Этот шаг выполняется только по требованию и подразумевает перемещение собранного артефакта в удаленный репозиторий. Развертывание позволяет команде разработчиков получить доступ к последней версии библиотеки через сеть. Удаленные репозитории часто используются внутри компаний для хранения внутренних инструментов или публикуются в интернете для широкого доступа.


Зависимости

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

В Java нет встроенной системы зависимостей (как в Go или Rust). Вместо этого есть инструменты сборки (Maven, Gradle), которые опираются на соглашения. Принцип координат в Maven (Maven Coordinates) определяет, что каждая зависимость имеет три значения:

groupId:artifactId:version

К примеру:

org.springframework:spring-core:5.3.27

Внутри это работает так:

  1. pom.xml декларирует всё, что нужно проекту.
  2. Репозиторий - это хранилище JAR-файлов (обычно Maven Central или частный Nexus/Artifactory).
  3. Локальный кэш (~/.m2/repository) хранит скачанные один раз JAR-файлы, на диске.
  4. Если A зависит от B, а B от C, то Maven сам скачает и C, и разрешит конфликты версий по стратегии "ближайший к корню". Это транзитивные зависимости.

То есть, зависимости не устанавливаются в систему, а привязываются к конкретному проекту.

Система сборки автоматически разрешает граф зависимостей, скачивая не только нужную библиотеку, но и все её внутренние зависимости.

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

  1. Проверяет локальный репозиторий на наличие указанной версии.
  2. Если версия найдена, она подключается к проекту.
  3. Если версия отсутствует, система обращается к удаленным репозиториям, перечисленным в конфигурации.
  4. Скачивает саму библиотеку и все её транзитивные зависимости (зависимости зависимостей).
  5. Сохраняет полученные файлы в локальный кэш для будущего использования.

Транзитивные зависимости создают сложную сеть связей между компонентами. Например, библиотека A зависит от библиотеки B, а библиотека B зависит от библиотеки C. При подключении A система автоматически подтянет B и C. Это упрощает жизнь разработчику, но иногда приводит к конфликтам версий, когда разные части проекта требуют разные версии одной и той же библиотеки.

Конфликты версий решаются механизмом приоритета. Maven выбирает первую зависимость, встретившуюся в дереве, или использует стратегию "ближайшей" зависимости. Если возникает конфликт, система выводит предупреждение в консоль, указывая, какая версия была выбрана и почему. Разработчик может явно указать желаемую версию в pom.xml, чтобы переопределить поведение по умолчанию.

Типы зависимостей определяются атрибутом scope:

  • compile: Стандартная зависимость, доступна на всех этапах.
  • test: Доступна только во время компиляции и запуска тестов.
  • provided — Предполагается, что среда выполнения предоставит библиотеку (например, сервер приложений).
  • runtime: Не нужна для компиляции, но требуется во время выполнения.
  • Система: Зависимость находится в локальной файловой системе и не скачивается из репозитория.

Maven

Как работает Maven?

Maven строится вокруг концепции Project Object Model (POM) — описания проекта в XML-формате. Файл pom.xml содержит метаданные, зависимости, плагины и настройки сборки. Проект следует стандартной структуре каталогов, что позволяет инструменту автоматизировать все этапы разработки без дополнительной конфигурации.

pom.xml содержит:

  • метаданные (название, версия, автор);
  • зависимости (библиотеки);
  • плагины (для сборки, тестирования);
  • настройки профилей (dev, prod).

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

Maven делегирует компиляцию плагину maven-compiler-plugin. При выполнении фазы compile плагин:

  1. Сканирует каталог src/main/java рекурсивно
  2. Формирует список .java файлов
  3. Вызывает компилятор javac с параметрами:
    • -source и -target из свойств проекта
    • -encoding из project.build.sourceEncoding
    • -classpath на основе разрешённых зависимостей
  4. Размещает .class файлы в target/classes с сохранением структуры пакетов

Компиляция тестов происходит отдельно через фазу test-compile с использованием каталога src/test/java и размещением результатов в target/test-classes. Класспат для тестов включает основные классы и зависимости с областью test.

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

  • глобальный терминал (cmd, bash, zsh) читает PATH, где прописан Maven;
  • IDE (IntelliJ IDEA, VS Code, Eclipse) запускает свой встроенный терминал с тем окружением, с которым запущена сама IDE.

Если вы установили Maven после запуска IDE, то терминал IDE об этом не знает. Если вы запустили IDE не из терминала, она не наследует ваши shell-конфиги(.bashrc, .zshrc). В Windows разница между системными и пользовательскими переменными.

Лечится такое перезапуском IDE или ручным указанием PATH внутри терминала IDE.

Чтобы загрузить все зависимости, нужно написать команду:

mvn clean install

Разбор:

  • mvn запускает Maven в текущем проекте и читает его pom.xml.
  • Фаза clean удаляет каталог target, чтобы убрать артефакты предыдущих сборок.
  • Фаза install запускает полный цикл до установки и кладёт готовый артефакт в локальный репозиторий ~/.m2/repository.
  • Команда удобна для проверки, что проект полностью собирается "с нуля" и доступен другим локальным модулям.

Здесь:

  • clean удаляет target/ (старые class-файлы);
  • install собирает проект и кладёт результат в локальный репозиторий.

Чтобы узнать точные названия и версии, нужно идти:

  • в Maven Central Search;
  • MVNRepository.com;
  • или искать внутри IDE, к примеру IDEA умеет искать зависимости по имени.

Версионность

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

mvn clean install -U

Разбор:

  • Ключ -U означает --update-snapshots и форсирует обновление SNAPSHOT-зависимостей.
  • Команда полезна, когда в удалённом репозитории уже появился более свежий snapshot, а локальный кэш ещё не обновился.
  • Флаг не меняет версии в pom.xml, он только принудительно перепроверяет и скачивает артефакты.
  • Остальная часть команды (clean install) выполняет стандартный цикл очистки, сборки, тестов и установки.

Флаг -U (--update-snapshots) — заставляет перекачать SNAPSHOT-зависимости (нестабильные версии).

Если зависимость не будет найдена, то Maven идёт в репозитории, объявленные в pom.xml / settings.xml.

Но Maven никогда не обновляет версию автоматически. Если нужно перейти с 1.0 на 1.1 — надо изменить pom.xml вручную.

Проверка репозиториев и зависимостей:

mvn help:effective-pom
mvn dependency:tree

Разбор:

  • help:effective-pom показывает итоговый POM после применения родительских POM, профилей и значений по умолчанию.
  • dependency:tree строит дерево зависимостей и помогает увидеть транзитивные библиотеки.
  • Эти команды часто применяют при диагностике конфликтов версий и неожиданных подключений.
  • Они не изменяют проект, а дают диагностическую картину текущей конфигурации.

Если будет опечатка в группе или артефакте, то Maven не соберёт проект.

Неизбежные сложности с настройкой версий и зависимостей являются нормальной частью работы в Java. Экосистема огромна и разнообразна, поэтому периодическая "танцевальная" настройка окружения неизбежна. Разработчики сталкиваются с необходимостью обновлять JDK, менять версии Maven, корректировать pom.xml и решать конфликты версий.

Ключевой параметр в pom.xml говорит "компилируй так, чтобы работало на этой версии":

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

Разбор:

  • Блок <properties> задаёт общие параметры, которые используют плагины сборки.
  • maven.compiler.source определяет, какой синтаксис языка разрешён компилятору.
  • maven.compiler.target задаёт формат байткода, совместимый с указанной версией JVM.
  • Эти свойства помогают синхронизировать ожидания проекта и фактическую JDK в окружении сборки.

Одной из распространенных проблем при сборке является несоответствие версий Java, Maven и зависимостей. Каждый компонент экосистемы имеет свои требования к версии языка и инструментария. Компилятор Java требует указания целевой версии (target), версии исходного кода (source) или единого параметра release. Если эти параметры не согласованы с установленным JDK, который реально запускает javac, сборка падает.


Ошибки "release version … not supported" и "invalid target release"

Maven и IDE передают компилятору флаг --release N (или пары -source / -target). Сообщения выглядят так:

error: release version 25 not supported

или (в старых логах Maven):

invalid target release: 25

Смысл одного и того же: JDK, которым идёт компиляция, не умеет собирать проект под указанную версию N.

СитуацияЧто в pom.xmlКакой JDK запускает сборкуЧто сделать
Цель слишком новаяmaven.compiler.release 25, source/target 25JDK 17 или 21Поставить JDK ≥ 25 или снизить release до 17/21
Цель слишком стараяrelease 5, source/target 1.5JDK 21 и новееПоднять минимум до 8 (JDK 21+ не компилирует в Java 5/6)
Разные JDK в терминале и IDEВ pom — 21В терминале JDK 17, в IDEA — 21Выровнять Project SDK / JAVA_HOME и java -version
Устаревший maven-compiler-pluginВ pom указана Java 25JDK 25 установленЯвно указать плагин 3.13+ (см. ниже)

Проверка окружения:

java -version
javac -version
mvn -version

Разбор:

  • java -version показывает версию рантайма JVM, которая запускает Java-приложения.
  • javac -version показывает версию компилятора.
  • mvn -version дополнительно выводит, какой Java runtime использует сам Maven.
  • Сравнение этих трёх значений позволяет быстро обнаружить рассинхрон IDE/терминала и причину ошибок компиляции.

В выводе mvn -version смотрите строку Java version — именно этот JDK использует Maven из терминала.

Рекомендуемая настройка в pom.xml (одно значение вместо пары source/target):

<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

Разбор:

  • maven.compiler.release объединяет контроль исходного синтаксиса и целевого байткода одним параметром.
  • Это более надёжный современный вариант, чем отдельные source и target.
  • project.build.sourceEncoding фиксирует кодировку исходников и ресурсов, чтобы избежать проблем с текстом на разных ОС.
  • Такой набор снижает риск нестабильных сборок в команде и CI.

Явное объявление плагина, если свойства не подхватываются или нужна свежая Java:

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>21</release>
</configuration>
</plugin>
</plugins>
</build>

Разбор:

  • Секция <build><plugins> подключает плагины, расширяющие стандартный lifecycle Maven.
  • maven-compiler-plugin отвечает за вызов javac.
  • <version> фиксирует конкретную версию плагина, что делает сборку воспроизводимой.
  • <configuration><release>21</release> принудительно задаёт целевую версию Java для компиляции.
  • Явная конфигурация особенно полезна в проектах с разными JDK на машинах разработчиков.

Не указывайте в pom.xml версию Java выше, чем установленный JDK (например, 25 при JDK 17), если вы не планируете сразу обновлять среду. Для учебных и корпоративных проектов на 2026 год разумные значения — 17 или 21.

После смены версии в pom.xml выполните mvn clean compile и перезапустите IDE, чтобы подтянулся тот же JDK.

Настройки версий в pom.xml (классический вариант через source/target):

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
</properties>

Эти свойства сообщают Maven, какую версию Java использовать для компиляции. Если проект использует функции, появившиеся в Java 17 (например, switch выражения или var), а целевая версия стоит на уровне 8, компилятор выдаст ошибку синтаксиса — даже при "свежем" JDK.

Минимальная версия Maven также важна. Новые версии Maven поддерживают новые возможности синтаксиса pom.xml и улучшают работу с зависимостями. Старые версии Maven могут не понимать современные конструкции, что приведет к ошибкам парсинга. Рекомендуется использовать актуальную стабильную версию инструмента.

Версии зависимостей должны соответствовать версии Java. Некоторые библиотеки требуют минимум Java 11 или Java 17 для корректной работы. Попытка запустить такую библиотеку на Java 8 вызовет ошибку времени выполнения NoSuchMethodError или ClassNotFoundException. Документация каждой библиотеки содержит информацию о требуемой версии платформы.

Если разработчик не знает точного названия, группы или версии зависимости, поиск должен проводиться через специализированные ресурсы. Основным источником информации является сайт Maven Central Search. На нем доступен полный индекс всех публичных библиотек. Пользователь вводит ключевые слова, описывающие функционал, который нужен (например, "JSON parsing" или "HTTP client"), и получает список подходящих вариантов.

Результаты поиска показывают:

  • groupId: Группа организации-разработчика.
  • artifactId: Имя самой библиотеки.
  • version: Доступные версии.
  • Описание: Краткое пояснение назначения библиотеки.

Альтернативный способ поиска — использование автозаполнения в IDE. При попытке подключить класс из неизвестной библиотеки среда разработки подсказывает возможные варианты импорта. Инструмент автоматически генерирует нужный блок <dependency> в pom.xml с правильными параметрами. Это избавляет от необходимости вручную искать названия и проверять их правильность.

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

Локальный JAR в репозиторий Maven:

mvn install:install-file \
-Dfile=lib/mylib.jar -DgroupId=com.example -DartifactId=mylib \
-Dversion=1.0 -Dpackaging=jar

Разбор:

  • Цель install:install-file добавляет внешний локальный JAR в Maven-репозиторий пользователя.
  • -Dfile указывает путь к самому бинарному файлу.
  • -DgroupId, -DartifactId, -Dversion формируют Maven-координаты артефакта.
  • -Dpackaging=jar задаёт тип артефакта.
  • После установки такой артефакт можно подключать в pom.xml как обычную зависимость.

Механизм обновления зависимостей базируется на регулярной сверке версий в локальном кэше с версиями в удаленных хранилищах. По умолчанию Maven проверяет наличие новых версий раз в день или при наличии специального флага. При запуске процесса система отправляет запрос к указанному в pom.xml репозиторию (например, Maven Central). Сервер возвращает список доступных версий артефакта.

Если локальная версия отличается от доступной, система предлагает обновление. Однако по умолчанию Maven не обновляет зависимости автоматически, если они уже присутствуют в кэше. Это сделано для стабильности сборки: случайное обновление библиотеки может сломать работающий код. Для форсирования проверки используется флаг -U или настройка <updateSnapshots>true</updateSnapshots> в файле конфигурации.

Проверка репозиториев происходит в строгом порядке. Сначала система обращается к локальному репозиторию. Если файл найден, он используется. Если нет, идет очередь к удаленным хранилищам, перечисленным в settings.xml или pom.xml. Обычно первым указывается официальный репозиторий Maven Central, затем корпоративные хранилища или приватные зеркала. Если ни один из источников не содержит требуемый файл, сборка прерывается с ошибкой.

При работе с SNAPSHOT-версиями (разработочными версиями) проверка происходит чаще. Эти версии помечаются суффиксом -SNAPSHOT и предполагают постоянные изменения. Система обязана проверять их наличие в репозитории каждый раз перед сборкой, чтобы получить актуальный код. Обычные релизные версии (1.0.0, 2.3.1) считаются неизменными и не требуют постоянной проверки после первого скачивания.

Полная пересборка с обновлением SNAPSHOT-зависимостей:

mvn clean install -U

Разбор:

  • Это полный цикл сборки с принудительным обновлением snapshot-артефактов.
  • Команда часто используется в CI и при отладке проблем с "залипшим" локальным кэшем.
  • Сначала очищается target, затем пересчитываются зависимости, запускаются тесты и выполняется локальная установка.
  • Флаг -U влияет только на обновление зависимостей, а не на изменение версий проекта.

CLI-команды Maven

mvn clean
mvn compile
mvn test
mvn package
mvn install
mvn deploy
mvn dependency:tree
mvn dependency:resolve
mvn clean install -U
mvn package -DskipTests
mvn -T 1C package

Разбор:

  • Блок перечисляет самые частые команды жизненного цикла Maven.

  • Последовательность clean → compile → test → package → install → deploy отражает путь от исходников до публикации артефакта.

  • dependency:tree и dependency:resolve помогают анализировать граф зависимостей.

  • -DskipTests ускоряет сборку, если нужно временно пропустить тестовый этап.

  • -T 1C включает параллельное выполнение задач с числом потоков по числу ядер CPU.

  • clean — удаляет target/.

  • compile / test / package / install / deploy — фазы жизненного цикла.

  • dependency:tree / dependency:resolve — граф и кэш зависимостей.

  • -DskipTests — пропуск тестов; -Dmaven.test.skip=true — только запуск тестов.

  • -T 1C — параллельная сборка по числу ядер.


pom.xml

Базовый файл pom.xml содержит следующие секции:

Код ITЗагрузка примера кода…

Разбор:

  • Корневой тег <project> и namespace определяют формат POM-модели.
  • Блок координат (groupId, artifactId, version) формирует уникальный идентификатор артефакта.
  • Секция <dependencies> описывает внешние библиотеки и их scope.
  • Секция <build><plugins> задаёт поведение сборки, включая компиляцию и дополнительные шаги.
  • maven-compiler-plugin в примере фиксирует версию Java для компиляции исходного кода.

Секция <properties> задаёт переменные, используемые в других частях файла. Плагины в секции <build> расширяют стандартный жизненный цикл — компилятор настраивает версию Java, Surefire запускает тесты, Assembly создаёт исполняемые JAR-файлы.


Фазы Maven

Maven работает по жизненному циклу сборки проекта, который состоит из фаз. Каждая фаза представляет собой этап в процессе создания артефакта:

  • validate - проверяет корректность проекта и наличия необходимых данных;
  • compile - компилирует исходный код проекта;
  • test – запускает юнит-тесты;
  • package - упаковывает скомпилированный код в JAR/WAR/SO и т.д.;
  • verify - выполняет проверки после упаковки перед установкой;
  • install - устанавливает артефакт в локальное хранилище Maven;
  • deploy - передаёт артефакт в удалённый репозиторий (например, Nexus, Artifactory).

Можно вызвать одну из фаз:

mvn compile
mvn package
mvn install

mvn package (см. блок выше) запускает все предшествующие фазы. Пропуск тестов:

mvn package -DskipTests

Файлы проекта распределены по строгой стандартной структуре:

ПутьНазначение
/ПроектОбщая папка проекта
/Проект/pom.xmlГлавный конфигурационный файл (Maven)
/Проект/srcДиректория, где хранится весь исходный код Java-проекта
/Проект/src/mainОсновной код проекта
/Проект/src/main/javaИсходные файлы Java, организованные в пакетной структуре
/Проект/src/main/java/com/mycompany/projectПример пакета в проекте
/Проект/src/main/resourcesРесурсы: конфигурационные файлы, локализация, изображения, CSS, JS и другие не-кодовые файлы
/Проект/src/testТестовый код, не включаемый в финальную сборку
/Проект/src/test/javaТестовые классы (например, с использованием JUnit, TestNG)
/Проект/src/test/resourcesКонфигурация и ресурсы, используемые при тестировании
/Проект/targetКаталог для автоматически генерируемых файлов (сборка, артефакты, временные файлы)

Структура каталогов Maven

my-app/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── app/
│ │ │ └── Main.java
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── app/
│ │ └── MainTest.java
│ └── resources/
│ └── test-config.properties
└── target/
├── classes/
├── test-classes/
└── my-app-1.0.0.jar

Каталог target создаётся автоматически при сборке и содержит все промежуточные и финальные артефакты. Его содержимое удаляют через mvn clean (см. блок CLI ниже).


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

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

  • groupId — организация или домен владельца библиотеки
  • artifactId — имя библиотеки
  • version — конкретная версия

Пример объявления зависимости в pom.xml:

<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>

При первой сборке Maven проверяет локальный репозиторий ~/.m2/repository. Если артефакт отсутствует, инструмент обращается к центральному репозиторию по адресу https://repo.maven.apache.org/maven2, скачивает библиотеку и все её транзитивные зависимости, сохраняя их локально. Последующие сборки используют кэшированные файлы без сетевых запросов.

Транзитивные зависимости разрешаются автоматически. Если библиотека A зависит от B версии 2.0, а проект напрямую использует B версии 1.5, Maven применяет правила разрешения конфликтов для выбора единой версии.

Maven разрешает зависимости в три этапа:

  1. Анализ секции <dependencies> в pom.xml
  2. Рекурсивное извлечение транзитивных зависимостей из метаданных артефактов
  3. Применение правил разрешения конфликтов версий:
    • Ближайший путь в дереве зависимостей
    • Явное указание версии в <dependencyManagement>
    • Порядок объявления в родительском POM

Зависимости классифицируются по области действия (scope):

  • compile — доступны во время компиляции и выполнения (значение по умолчанию)
  • test — только для тестов, не попадают в финальный артефакт
  • runtime — не нужны для компиляции, требуются при запуске
  • provided — предоставляются окружением выполнения (например, сервлеты в веб-контейнере)

Артефакт Maven

Артефакт в Maven – это результат сборки проекта, например, JAR-файл, который содержит код, библиотеки, ресурсы и метаданные.

Каждый артефакт имеет уникальный идентификатор, состоящий из элементов:

  • groupId – com.example – организация или домен;
  • artifactId – my-app – имя проекта или библиотеки;
  • version – 1.0.0., 2.1.3-SNAPSHOT – версия.

Пример:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>

Это будет ссылка на артефакт:

org.springframework.boot:spring-boot-starter-web:2.7.0

Артефакты бывают следующих видов:

  • .jar – обычные Java-библиотеки;
  • .war – веб-приложения;
  • .pom – описание проекта;
  • .aar – Android-библиотеки..

Репозиторий

Репозиторий – это место, где хранятся артефакты. Они могут храниться локально (локальный репозиторий), в общедоступном репозитории Maven (центральный), и в удалённом (частном), для приватных артефактов.

При первом использовании зависимости Maven скачивает её из центрального репозитория, и сохраняет в локальный. При повторном использовании уже берёт из локального. То есть, добавляя зависимость в pom.xml, мы скачиваем и сохраняем локально в папку ~/.m2/repository.


Пошаговое создание проекта Maven

  1. Установите Apache Maven и проверьте доступность в PATH:
mvn --version
  1. Выполните генерацию шаблона проекта:
mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=my-app \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
  1. Перейдите в созданную директорию:
cd my-app
  1. Откройте файл src/main/java/com/example/App.java и замените содержимое:
package com.example;

public class App {
public static void main(String[] args) {
System.out.println("Привет из Maven!");
}
}
  1. Соберите проект:
mvn package
  1. Запустите приложение:
java -cp target/my-app-1.0.0.jar com.example.App

Начиная с Java 11 JavaFX не входит в JDK — Maven подтягивает его как зависимость; в IDEA может понадобиться Reload Maven Projects. Запуск через Maven:

mvn javafx:run

Точка входа в Maven (Main-Class)

В pom.xml нет отдельного поля "точка входа". При сборке плагины записывают в манифест JAR атрибут Main-Class — полное имя класса с методом public static void main(String[] args). См. также манифест JAR и синтаксис main в основах Java.

Практика в NetBeans и shade-плагин для запуска вне IDE: Maven в NetBeans.


Обычный JAR — maven-jar-plugin

Для проекта с пакетом org.coolcompany и классом Main добавьте или измените блок <build>:

Код ITЗагрузка примера кода…

Чтобы запускать другой класс (например, Test в том же пакете), создайте Test.java с тем же сигнатурой main и замените значение <mainClass> на org.coolcompany.Test. Метод test() сам по себе точкой входа не является — JVM ищет именно main.

Сборка и запуск:

mvn clean package
java -jar target/my-app-1.0.0.jar

Проверка: откройте JAR как ZIP и посмотрите META-INF/MANIFEST.MF — там должна быть строка Main-Class: org.coolcompany.Test (или выбранный класс).


Fat JAR (зависимости внутри одного файла)

Если нужен исполняемый архив со всеми библиотеками, используйте maven-shade-plugin и укажите mainClass в ManifestResourceTransformer (подробный пример — в Maven в NetBeans).


Запуск без пересборки — exec-maven-plugin

Удобно при разработке: класс для запуска задаётся в плагине, pom.xml менять реже, чем при смене mainClass в jar-plugin.

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<mainClass>org.coolcompany.Test</mainClass>
</configuration>
</plugin>
mvn exec:java

В IntelliJ IDEA можно запустить любой класс с main через Run без правок pom.xml.


Spring Boot

Точка входа обычно определяется классом с @SpringBootApplication. Явная настройка в pom.xml нужна редко; см. Spring Boot.

СценарийПлагинЗапуск
Обычный JARmaven-jar-pluginjava -jar target/....jar
JAR + все зависимостиmaven-shade-pluginjava -jar target/....jar
Быстрый запуск при разработкеexec-maven-pluginmvn exec:java
Spring Bootspring-boot-maven-pluginmvn spring-boot:run

Gradle

Практический старт: Gradle — практический старт.


Как работает Gradle?

Gradle – система автоматической сборки, построенная с учетом принципов Maven, но предоставляющая дополнительные возможности на языках Groovy и Kotlin вместо традиционной XML-образной формы представления конфигурации проекта. Это позволяет писать логику сборки как обычный код — использовать переменные, циклы, условия и функции.

Основные компоненты проекта Gradle:

  • build.gradle или build.gradle.kts — основной файл сборки
  • settings.gradle — настройки мультипроектной структуры
  • gradle/ — обёртка Gradle для кроссплатформенного запуска без глобальной установки

Gradle вводит концепцию задач (Задачи) как атомарных единиц работы. Задачи могут зависеть друг от друга, формируя направленный ациклический граф выполнения. Инкрементальная сборка анализирует изменения входных файлов и пересобирает только затронутые модули, что ускоряет повторные сборки.

Принцип разделения на src/main и src/test аналогичен Maven, но конфигурация хранится в build.gradle, а не в pom.xml. Отличие есть в папке для автоматически сгенерированных файлов – в Maven они хранятся по пути /target, а в Gradle - /build.

Gradle использует задачу compileJava из плагина java. Процесс аналогичен Maven, но с дополнительными оптимизациями:

  1. Инкрементальная компиляция — анализ изменений на уровне классов
  2. Демон компиляции — фоновый процесс, сохраняющий состояние между запусками
  3. Параллельная компиляция — использование нескольких ядер процессора

Настройка компилятора происходит через блок toolchain, который автоматически выбирает установленную JDK подходящей версии или загружает её при отсутствии. Это устраняет зависимость от переменной окружения JAVA_HOME.


build.gradle

Конфигурация на Groovy DSL:

Код ITЗагрузка примера кода…

Конфигурация на Kotlin DSL (build.gradle.kts):

Код ITЗагрузка примера кода…

Оба формата эквивалентны по функционалу. Kotlin DSL обеспечивает строгую типизацию и автодополнение в IDE, Groovy DSL — более лаконичный синтаксис.


CLI-команды Gradle

Управление жизненным циклом:

  • ./gradlew clean — удаляет каталог build;
./gradlew clean
  • ./gradlew build — выполняет полную сборку (компиляция, тестирование, создание артефакта);
./gradlew build
  • ./gradlew compileJava — компилирует только основной код;
./gradlew compileJava
  • ./gradlew test — запускает только тесты;
./gradlew test
  • ./gradlew jar — создаёт JAR-файл без выполнения тестов;
./gradlew jar
  • ./gradlew publishToMavenLocal — устанавливает артефакт в локальный кэш Maven (~/.m2/repository);
./gradlew publishToMavenLocal
  • ./gradlew run — запускает приложение (требует плагин application).
./gradlew run

Информационные команды:

  • ./gradlew Задачи --all — отображает все доступные задачи с описанием;
./gradlew Задачи --all
  • ./gradlew dependencies — выводит дерево зависимостей.
./gradlew dependencies

Полезные флаги и режимы:

-x test — пропускает выполнение тестов (например, ./gradlew build -x test); --parallel — включает параллельное выполнение задач; --continuous (или -t) — непрерывный режим: сборка перезапускается при изменении исходных файлов.


Сборка в Gradle

Стандартный жизненный цикл:

gradle build

Этапы:

  1. classes — компиляция основного кода
  2. testClasses — компиляция тестов
  3. test — выполнение тестов
  4. jar — создание JAR-артефакта
  5. assemble — агрегация всех артефактов

Результаты сохраняются в каталоге build:

build/
├── classes/
│ ├── java/main/ # скомпилированные классы
│ └── java/test/ # скомпилированные тесты
├── libs/
│ └── my-app-1.0.0.jar
├── reports/tests/ # отчёты о тестировании
└── tmp/ # временные файлы

Gradle отслеживает состояние входных и выходных файлов каждой задачи. При повторной сборке без изменений исходного кода задачи помечаются как актуальные (UP-TO-DATE) и пропускаются.

Gradle использует конфигурации вместо областей действия:

  • implementation — зависимости для компиляции и выполнения, скрыты от потребителей библиотеки
  • api — зависимости, видимые потребителям (для библиотек)
  • testImplementation — зависимости только для тестов
  • runtimeOnly — зависимости только для выполнения

Преимущество подхода — контроль видимости транзитивных зависимостей. При использовании implementation зависимости не "утекают" в проекты, использующие вашу библиотеку, что снижает риск конфликтов версий.

Разрешение зависимостей происходит лениво — только при выполнении задач, требующих класспат. Это ускоряет запуск Gradle для операций, не связанных со сборкой (например, gradle Задачи).

gradle Задачи

Запуск проекта Gradle

Gradle предоставляет несколько способов запуска приложения:

  • Через задачу run плагина application:
gradle run
  • Через обёртку (без глобальной установки Gradle):
./gradlew run # Linux/macOS
gradlew.bat run # Windows
  • Создание исполняемого JAR с манифестом:
jar {
manifest {
attributes 'Main-Class': 'com.example.App'
}
}

Запуск после сборки:

java -jar build/libs/my-app-1.0.0.jar

Обёртка gradlew скачивает указанную версию Gradle при первом запуске и использует её для всех последующих сборок, гарантируя воспроизводимость независимо от окружения разработчика.


Сравнение Maven и Gradle

Краткое сравнение Maven и Gradle

КритерийMavenGradle
Тип конфигурацииXMLGroovy/Kotlin DSL
Файл конфигурацииpom.xmlbuild.gradle

Команды в Maven и Gradle

КритерийMavenGradle
Компиляцияmvn compilegradle compileJava
Запуск тестовmvn testgradle test
Сборка JAR/WARmvn packagegradle build
Установка в локальный кэшmvn installgradle publishToMavenLocal
Очистка кэшаmvn cleangradle clean
Запуск приложенияmvn exec:javagradle run
Пропустить тестыmvn install -DskipTestsgradle build -x test
Обновить зависимостиmvn dependency:resolvegradle dependencies
Создать проектmvn archetype:generategradle init
Показать список задачmvn help:describegradle Задачи

Аналогичные секции в pom.xml и build.gradle

MavenGradle
<groupId>group
<artifactId>Имя проекта (из settings.gradle)
<version>version
<properties>Переменные в ext {}
<dependencies>dependencies {}
maven-compiler-pluginjava.toolchain

Gradle стал стандартом для:

  • Android-разработки — официальная система сборки Android Studio
  • Spring Boot — генератор проектов start.spring.io предлагает Gradle по умолчанию
  • Мультипроектных систем — поддержка иерархии проектов с общими зависимостями
  • Кастомных сценариев — публикация в репозитории, генерация документации, деплой на серверы

Гибкость скриптов позволяет интегрировать любые внешние инструменты — запускать утилиты командной строки, обрабатывать файлы, взаимодействовать с веб-сервисами.


Структура проекта Gradle

my-app/
├── build.gradle # или build.gradle.kts
├── settings.gradle
├── gradlew
├── gradlew.bat
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── App.java
│ │ └── resources/
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── AppTest.java
│ └── resources/
└── build/ # генерируется автоматически

Каталог gradle/wrapper содержит обёртку для кроссплатформенного запуска. Файл gradle-wrapper.properties указывает версию Gradle:

distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip

Пошаговое создание проекта Gradle

  1. Установите Gradle или используйте обёртку из другого проекта.
  2. Инициализируйте новый проект:
gradle init --type java-application \
--project-name my-app \
--package com.example \
--dsl groovy
  1. Система предложит выбрать вариант тестового фреймворка (JUnit 4, JUnit 5, TestNG). Выберите JUnit Jupiter (JUnit 5).
  2. Откройте сгенерированный файл src/main/java/com/example/App.java:
package com.example;

public class App {
public String getGreeting() {
return "Привет из Gradle!";
}

public static void main(String[] args) {
System.out.println(new App().getGreeting());
}
}
  1. Соберите и запустите проект:
./gradlew build
./gradlew run

Gradle автоматически скачает зависимости из mavenCentral(), скомпилирует код с использованием JDK 17 (согласно настройкам toolchain), запустит тесты и выполнит основной класс. Все этапы логируются с указанием затраченного времени и статуса каждой задачи.


Работа с Java без систем сборки

Разработка на Java возможна без Maven и Gradle.

Минимальный рабочий процесс:

  1. Создайте структуру каталогов вручную:
mkdir -p src/com/example/app
mkdir -p bin
  1. Напишите класс src/com/example/app/Main.java:
package com.example.app;

public class Main {
public static void main(String[] args) {
System.out.println("Прямая компиляция");
}
}
  1. Скомпилируйте вручную:
javac -d bin -sourcepath src src/com/example/app/Main.java
  1. Запустите:
java -cp bin com.example.app.Main

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

Такой подход применим для учебных задач, скриптов или микропроектов. Для коммерческой разработки, командной работы и поддержки сложных систем ручное управление становится непрактичным из-за роста объёма рутинных операций и риска ошибок. Системы сборки автоматизируют повторяющиеся действия, обеспечивают воспроизводимость и интеграцию с инструментами непрерывной интеграции.


Практический ориентир по выбору

Чтобы материал из статьи быстрее "приземлялся" в реальную работу, удобно держать короткий маршрут выбора.

СитуацияЧто выбратьПочему
Учебный мини-проект, 1 модульMavenПредсказуемая структура и понятные фазы сборки
Нужен быстрый build и гибкие скриптыGradleИнкрементальная сборка и DSL под кастомные задачи
Большой корпоративный проектТо, что принято в командеВажнее единый стандарт и воспроизводимость CI
Временный экспериментjavac + javaМинимальный порог входа, без лишней инфраструктуры
Полезная стратегия для новичка

Сначала соберите один и тот же маленький проект и в Maven, и в Gradle. После этого сравнение pom.xml и build.gradle воспринимается гораздо легче, чем "в теории".


Типичные ошибки и быстрые проверки

  • mvn или gradle не найден в IDE-терминале — перезапустите IDE и проверьте PATH.
  • Версия JDK в IDE и в терминале отличается — сверяйте java -version и настройки SDK проекта.
  • Зависимость добавлена, но класс не импортируется — выполните обновление проекта (Reload / Reimport).
  • Сборка работает локально, падает в CI — зафиксируйте версию JDK и инструмента сборки явно.

Быстрый "диагностический минимум":

java -version
javac -version
mvn -version
./gradlew -v

Связанные статьи


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.