1. Сборка как часть приложения
Если вы только пришли из мира консольных программ, сборка может казаться чем-то вроде «ну это же просто нажать Run». Но как только у проекта появляется хотя бы один коллега (или второй компьютер), начинается классический сериал: «у меня работает, у тебя не работает, давай созвонимся, покажи экран». Spring Boot как платформа любит предсказуемость, а значит, требует и предсказуемой сборки.
Каркас проекта уже есть, но без внятных правил сборки он остаётся просто набором файлов. После генерации главный вопрос становится очень практичным: как сделать так, чтобы этот каркас одинаково собирался и запускался на любой машине.
Представьте, что catalog-service — это блюдо. Код — ингредиенты, Spring контейнер — кухня, а сборка — рецепт с температурой, временем и точными пропорциями. Если рецепт «примерный», то на одной плите получится нормально, а на другой — уголь. Поэтому на старте мы фиксируем три вещи: какой Gradle используется, какая Java используется и какие Boot-задачи доступны в проекте.
Чтобы не путать термины, держите в голове простую таблицу — она сильно экономит нервы:
| Штука | Что фиксирует | Где живёт | Как вы её запускаете/используете |
|---|---|---|---|
| Gradle Wrapper | Версию Gradle | Внутри репозитория проекта | ./gradlew ... |
| Java Toolchain | Версию Java для компиляции и задач Gradle | build.gradle.kts | Gradle применяет автоматически |
| Boot plugin | Boot-ориентированные задачи и соглашения | build.gradle.kts | bootRun, и др. |
Эти три точки потом сходятся в одном месте — в build.gradle.kts. Поэтому полезно сначала понять их роли по отдельности, а уже потом читать build-файл целиком. Именно поэтому сейчас важно не столько жать Run, сколько понять, на чём этот запуск вообще держится.
2. Gradle Wrapper: «Gradle, который приходит вместе с проектом»
В идеальном мире у всех стоит одинаковый Gradle, одинаковая Java и одинаковая удача. В реальном мире у каждого разработчика на ноутбуке свой «зоопарк»: у кого-то Gradle 8, у кого-то 9.4, кто-то вообще никогда его не ставил, потому что «у меня IntelliJ сам всё умеет». Gradle Wrapper — это способ сделать проект независимым от этих случайностей.
Wrapper — это не отдельная программа «вместо Gradle». Это набор файлов в вашем проекте, который гарантирует: когда вы запускаете сборку, вы запускаете нужную версию Gradle, выбранную самим проектом. И да, это одна из причин, почему в Boot-проектах принято писать в README команды именно с ./gradlew, а не просто gradle.
Когда вы генерируете проект в Initializr, он добавляет Wrapper автоматически — и это отлично, потому что руками новичок почти всегда что-нибудь забудет, а потом будет говорить: «у меня Gradle сломался», хотя на самом деле его… не было.
Мини-карта файлов Wrapper выглядит так:
catalog-service/
├── gradlew
├── gradlew.bat
└── gradle/
└── wrapper/
├── gradle-wrapper.properties
└── gradle-wrapper.jar
Здесь важно без мистики понимать роль каждого файла. gradlew — скрипт для Linux/macOS, gradlew.bat — для Windows. Их задача проста: взять настройки из gradle-wrapper.properties, скачать нужный Gradle, если его ещё нет в кеше, и запустить сборку. gradle-wrapper.jar — маленький «двигатель» Wrapper, который умеет скачивать и стартовать правильную версию Gradle.
Если вы думаете «а можно удалить gradle-wrapper.jar, он же бинарник, странно хранить в git?» — можно, но тогда вы вернётесь в каменный век: каждый будет запускать сборку как получится. Мы так не делаем. Мы хотим, чтобы catalog-service был переносимым.
Как Wrapper выбирает Gradle
Самый интересный файл тут — gradle/wrapper/gradle-wrapper.properties. Он маленький, но именно он отвечает за то, какую версию Gradle считает «правильной» ваш проект.
Типичный пример:
# Откуда Wrapper скачивает дистрибутив Gradle
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4-bin.zip
Обратите внимание на простую мысль: версия Gradle — это не «что у вас установлено», а «что написано в проекте». При первом запуске Wrapper скачает указанную сборку Gradle и положит её в кеш пользователя, обычно где-то в домашней директории, внутри ~/.gradle/.... Поэтому первый запуск может быть чуть дольше: это нормально, вы не сломали интернет.
Полезно заранее знать, где живёт кеш, чтобы не паниковать, когда Gradle «что-то долго качает». Упрощённо картина такая:
flowchart TD
A[Вы запускаете ./gradlew] --> B[Wrapper читает gradle-wrapper.properties]
B --> C{Нужная версия Gradle уже в кеше?}
C -- Да --> D[Запуск Gradle]
C -- Нет --> E[Скачивание gradle-9.4-bin.zip]
E --> F[Распаковка в ~/.gradle/wrapper/dists]
F --> D[Запуск Gradle]
Это и есть «воспроизводимость»: один и тот же проект всегда тянет одну и ту же версию Gradle, и сборка ведёт себя предсказуемо.
Правильный запуск Wrapper из корня
Wrapper запускается командами, которые выглядят одинаково на всех машинах, но с небольшим различием по ОС. Важное правило — запускать их из корня проекта, там, где лежит gradlew. Иначе Gradle может не найти ваш build.gradle.kts, settings.gradle.kts и вообще будет вести себя так, будто вы «пришли не на ту вечеринку».
Мини-набор команд, который стоит запомнить уже сейчас:
# Linux/macOS
./gradlew --version
# Windows (cmd)
gradlew.bat --version
# Windows (PowerShell)
.\gradlew --version
Обратите внимание: ./gradlew — это не «какая-то магическая точка и слэш». Это буквально «запусти файл gradlew из текущей папки». Если вы случайно напишете просто gradle, то запустите глобально установленный Gradle, если он у вас есть, и вся идея Wrapper будет уничтожена одним нажатием Enter. Так что да, ./gradlew — наш маленький ежедневный ритуал, который спасает от больших ежедневных проблем.
3. Java Toolchain: фиксируем версию Java
Вторая классическая боль новичка: «Я поставил Java, но проект всё равно не собирается» или «у меня Java 25, а Gradle пишет, что нужно 21». И тут важно понять: в реальности у вас может быть несколько Java одновременно, и это не ошибка, а нормальное состояние разработки. Проблема начинается, когда проект не говорит явно, какую именно Java он ожидает.
Java Toolchain — это механизм Gradle, который позволяет в build.gradle.kts явно сказать: «компилируй проект вот этой версией Java». И тогда сборка перестаёт зависеть от того, какая Java «случайно оказалась первой в PATH» или какую Java IDE подхватила «по умолчанию».
Здесь важно держаться одной линии: у проекта не должна внезапно появиться вторая «настоящая» версия Java. Если в Initializr выбрана Java 25, ту же версию фиксируем и в toolchain. Тогда у проекта не будет отдельной «формальной» Java в форме и отдельной «реальной» Java в сборке.
Минимальная настройка toolchain в build.gradle.kts
Toolchain настраивается в build.gradle.kts в блоке java. Это выглядит спокойно и не требует магии:
java {
toolchain {
// Явно фиксируем версию JDK для сборки (компиляции и Gradle-задач)
languageVersion = JavaLanguageVersion.of(25)
}
}
Что это означает по-человечески: «Gradle, когда будешь компилировать и запускать задачи, используй JDK 25 как целевую». При этом у вас на машине может быть и JDK 17, и 21, и 25, но сборка будет стараться использовать именно то, что вы указали.
Важно не путать это с тем, на какой Java запускается сам Gradle. Gradle тоже работает внутри JVM. Если у вас стоит совсем неподходящая Java для запуска Gradle, вы можете получить ошибку ещё до того, как toolchain начнёт работать. Это редкость, но если вдруг случилось — не надо паниковать: это не «Spring сломался», а просто «Gradle не смог стартовать на этой JVM».
Проверка применения toolchain
Новички часто делают настройку toolchain, а потом не понимают: «Оно вообще сработало или я просто красиво написал кусок текста в build.gradle.kts?» Хорошая новость: проверка существует, и она не требует философии.
Во‑первых, можно посмотреть, какую Java видит Gradle и на какой он вообще запустился:
./gradlew --version
Во‑вторых, в Gradle есть отчёт по toolchains, часто доступный как задача javaToolchains. Это удобный способ увидеть, какие JDK найдены и какая будет выбрана:
./gradlew -q javaToolchains
Если задача недоступна, это не катастрофа: тогда достаточно ./gradlew --version и проверки настроек IDE. Но когда javaToolchains есть, это прямо «рентген» вашей Java-части сборки.
Тут полезно удерживать простую мысль: toolchain — это про сборку, а не про то, что вы случайно видите в терминале после java -version. Терминал показывает «дефолтную Java пользователя», а Gradle toolchain — «Java, выбранную проектом».
4. Плагин org.springframework.boot: роль в сборке
Если Wrapper и toolchain отвечают за воспроизводимость сборки, то Boot plugin отвечает за то, чтобы она была Spring Boot‑ориентированной. Можно собрать Java-проект и без Boot plugin. Можно даже собрать web-приложение и без него, если очень хочется страдать. Но мы здесь учимся не страдать, а строить правильный baseline.
Boot plugin — это не «ещё одна зависимость». Это расширение Gradle, которое добавляет удобные задачи и соглашения именно для Boot-приложений: запуск через bootRun, сборка исполняемого jar через bootJar, правильная упаковка, понятные дефолты. Вы как бы говорите Gradle: «Это не просто Java-программа. Это Spring Boot‑приложение, будь добр, относись к нему соответствующим образом».
Подключаем Boot plugin и фиксируем версию Boot
Плагины подключаются в блоке plugins в build.gradle.kts. Минимально это выглядит так:
plugins {
// Базовые задачи Java-проекта: компиляция, тесты, сборка jar
java
// Spring Boot задачи и соглашения: bootRun, bootJar и т.п.
id("org.springframework.boot") version "4.0.3"
}
Смысл прост: java говорит Gradle «это Java-проект, включи компиляцию, тесты и стандартные задачи», а org.springframework.boot добавляет поверх этого Boot-специфику. Версия плагина обычно совпадает с версией Spring Boot, которую вы выбрали в Initializr.
И важная практическая мысль: не надо «подкручивать версию Boot на глаз». Если вы внезапно поменяете 4.0.3 на что-то вроде 4.0.99-rc1, потому что «хочу самое новое», то получите приключение. А приключения нам в этом курсе нужны строго в виде понимания, а не в виде багов.
Что добавляет Boot plugin
Часто кажется, что плагин — это что-то абстрактное. Поэтому давайте закрепим максимально предметно: Boot plugin добавляет задачи Gradle, которыми вы реально будете пользоваться.
| Task | Для чего нужна | Что вы получите в итоге |
|---|---|---|
| bootRun | Запуск Boot-приложения из исходников | Приложение стартует без ручного java -cp ... |
| bootJar | Сборка исполняемого Boot jar | Готовый артефакт, который можно запускать |
| test | Запуск тестов | Проверка, что проект хотя бы собирается и тесты проходят |
Как увидеть, что задача существует, не запуская её? Например, так:
./gradlew help --task bootRun
Если Gradle описал вам задачу bootRun, значит, Boot plugin подключён и работает. Это очень спокойная, «нестрессовая» проверка: вы ничего не собираете, ничего не запускаете, а просто спрашиваете у Gradle: «а у нас вообще есть такая кнопка?»
5. Общая схема baseline сборки
В начале пути легко воспринимать gradlew, toolchain и Boot plugin как три разрозненных заклинания. Но это, по сути, одна история: сделать сборку предсказуемой. Полезно увидеть весь конвейер целиком, иначе вы будете исправлять ошибки вслепую и говорить знаменитое: «я ничего не менял, оно само».
Попробуем собрать это в одну схему, как в инженерных документах:
flowchart TD
Dev[Разработчик] -->|запускает| W["./gradlew"]
W --> G[Gradle 9.4 из Wrapper]
G --> P[Plugins: java + org.springframework.boot]
P --> T[Tasks: compileJava / test / bootRun / bootJar]
T --> A[Запуск или сборка catalog-service]
Здесь всё честно и без мистики. Вы запускаете ./gradlew — это вход в сборку. Wrapper обеспечивает версию Gradle. Gradle читает build.gradle.kts, подключает плагины. Плагины добавляют задачи. Задачи либо запускают приложение (bootRun), либо собирают артефакт (bootJar). И только после этого появляется что-то, что можно назвать «работающим приложением».
Если запомнить эту цепочку, половина ранних проблем исчезает сама собой: вы понимаете, на каком шаге «сломалось», и перестаёте чинить всё подряд.
Мини‑проверка baseline в catalog-service
Очень хочется сразу нажать Run и увидеть «Started CatalogServiceApplication…». Это нормально, мы все такие. Но хороший инженерный старт — сначала убедиться, что инструменты на месте, а потом уже запускать. Здесь мы сделаем небольшую проверку: она занимает пару минут и экономит часы.
Первая команда проверяет, что Wrapper работает и Gradle запускается «через проект»:
./gradlew --version
Вторая команда полезна именно для темы сегодняшней лекции: вы увидите, какие Java toolchains доступны, или хотя бы убедитесь, что Gradle нормально видит вашу Java:
./gradlew -q javaToolchains
Третья команда проверяет Boot plugin «без риска»: мы не стартуем сервер и ничего не скачиваем сверх необходимого — просто смотрим, что задача есть:
./gradlew help --task bootRun
Если все три команды отработали без ошибок, вы в очень хорошей позиции. У вас есть воспроизводимая сборка, фиксированная Java-цель и Boot‑ориентированные задачи. Это и есть базовая build-гигиена проекта.
6. Типичные ошибки
Почти все проблемы с Wrapper, toolchain и плагинами возникают не потому, что вы «что-то не поняли», а потому, что у нас у всех есть привычки из других миров разработки. Кто-то привык, что «IDE сама соберёт», кто-то — что java -version показывает истину, кто-то — что «лишние файлы можно удалить». Давайте разберём самые частые грабли, чтобы вы узнавали их с первого взгляда.
Ошибка №1: запускать gradle вместо ./gradlew.
Если вы запускаете глобальный gradle, вы выходите из мира воспроизводимости. У вас может быть Gradle другой версии, и вы получите странные ошибки: от несовместимости плагинов до «почему у меня нет bootRun». Лечится очень просто: в проектах курса всегда начинаем команды с ./gradlew (или .\gradlew в PowerShell).
Ошибка №2: запускать Wrapper не из корня проекта.
Когда вы запускаете команды из другой папки, Gradle может не найти build.gradle.kts и settings.gradle.kts или найдёт «не тот проект». Симптомы бывают смешные: от «Task not found» до ощущения, что Gradle «не видит зависимости». Привычка простая: сначала cd catalog-service, потом ./gradlew ....
Ошибка №3: удалять файлы Wrapper, потому что «мусор» или «бинарники в git — плохо».
gradlew, gradlew.bat, gradle-wrapper.jar, gradle-wrapper.properties — это не мусор, а часть проекта. Если их удалить, вы превращаете сборку в лотерею. В учебном проекте мы хотим, чтобы любой человек мог клонировать репозиторий и собрать его без установки «правильного Gradle» вручную.
Ошибка №4: путать «Java в терминале» и «Java, которую использует сборка».
Команда java -version показывает, какая Java первая в PATH в вашем текущем терминале. Но Gradle toolchain может выбирать другую Java. Это не баг и не заговор — как раз наоборот, это механизм контроля. Если вы видите несоответствие, не пугайтесь: проверяйте ./gradlew --version и, если доступно, javaToolchains.
Ошибка №5: не фиксировать Java Toolchain и надеяться на удачу IDE.
Сегодня оно собралось, завтра вы обновили IDE, послезавтра у коллеги другой JDK — и всё поехало. Toolchain нужен не потому, что вы «не доверяете себе», а потому, что проект должен иметь чёткий контракт по Java. Для Boot‑проекта это базовая гигиена, как мыть руки перед готовкой.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ