1. Первый рабочий цикл как навык
Самая частая ловушка новичка звучит так: «Я хочу быстрее писать контроллеры, а не эти ваши сборки». И это желание понятно. Но реальность проста: backend-сервис живёт не только в редакторе кода. Он живёт в сборке, в запуске, в воспроизводимости окружения и в умении быстро понять, что происходит. Если вы не умеете уверенно запускать проект, любой следующий шаг превращается в лотерею: то ли вы ошиблись в коде, то ли IDE «не так настроена», то ли Gradle «обиделся».
К этому моменту catalog-service уже перестал быть просто сгенерированным zip-архивом: у него есть понятные правила сборки и читаемая структура. Теперь важно пройти один спокойный рабочий цикл, чтобы проект не только читался, но и уверенно запускался.
Мы уже умеем читать build.gradle.kts и ориентироваться в каркасе проекта. Теперь нужен самый приземлённый шаг: превратить это в повторяемый рабочий цикл, который одинаково работает и из IDE, и из терминала.
Поэтому мы фиксируем «рабочий цикл» как навык, а не как случайный набор действий. Хороший рабочий цикл похож на привычный маршрут домой: вы не каждый раз заново выбираете, по какой улице идти. Вы знаете последовательность, и она повторяется. Для catalog-service наш минимальный цикл сегодня выглядит так: импорт проекта как Gradle-модели → синхронизация → просмотр задач → просмотр зависимостей → запуск через bootRun. И это тот фундамент, на котором потом вырастет всё остальное.
Когда этот цикл повторяется без сюрпризов, зависимости и стартеры перестают выглядеть магией: вы уже знаете, где их увидеть и чем проверить результат.
Чтобы держать это в голове, полезно один раз увидеть процесс как схему:
flowchart TD
%% Весь цикл нарисован как последовательность действий, которые вы повторяете каждый день
A["Импорт в IDE как Gradle-проект"] --> B["Gradle sync (модель проекта из build.gradle.kts)"]
B --> C["./gradlew tasks (какие операции доступны)"]
C --> D["./gradlew dependencies (какие библиотеки в проекте)"]
D --> E["./gradlew bootRun (запуск приложения)"]
E --> F["Остановить приложение (Ctrl+C)"]
2. Импорт catalog-service в IDE
Когда проект уже сгенерирован, очень хочется сделать «быстро и просто»: открыть папку, нажать зелёный треугольник возле main() и считать, что жизнь удалась. Иногда это даже сработает… но у такого подхода плохая привычка: он делает IDE главным источником правды. А главный источник правды у нас — Gradle-модель, то есть build.gradle.kts + Wrapper + настройки toolchain. IDE должна читать проект из этой модели, а не жить в параллельной реальности.
Представьте, что build.gradle.kts — это рецепт, а IDE — кухонная плита. Рецепт говорит, что мы готовим и в каких пропорциях. Если плита «придумала» рецепт сама, вы можете получить вкусное блюдо… или неожиданно сварить компот из зависимостей. Поэтому импортируем проект так, чтобы IDE увидела: «Ага, это Gradle-проект, вот Wrapper, вот плагины, вот задачи, вот зависимости».
Поскольку у всех разные IDE, я опишу принцип, который везде один и тот же, а детали кнопок вы легко узнаете по словам Gradle и Import.
Вы выбираете импорт проекта как Gradle-проекта, указываете корень проекта — ту папку, где лежат build.gradle.kts и gradlew, — и соглашаетесь использовать Gradle Wrapper. Это важный момент: IDE должна запускать сборку через gradlew, а не через «встроенный Gradle неизвестной версии». Затем IDE предложит выбрать JDK для импорта. Даже если в build.gradle.kts настроен toolchain, для самой IDE всё равно нужен JDK, чтобы анализировать код и запускать Gradle. Обычно выбирают тот же, что и в toolchain, — в нашем дне это Java 25.
Чтобы быстро проверить, что вы действительно импортировали правильный корень, просто посмотрите на структуру проекта. В корне должны быть как минимум:
catalog-service/
├── gradlew
├── gradlew.bat
├── build.gradle.kts
├── settings.gradle.kts
├── gradle/
│ └── wrapper/
│ └── gradle-wrapper.properties
└── src/
├── main/
│ └── java/...
└── test/
└── java/...
Если вы импортировали не ту папку (например, папку на уровень выше, где лежат другие проекты), IDE начнёт вести себя странно: задачи не находятся, bootRun не появляется, зависимости «не подтягиваются», и вы начинаете подозревать, что Spring Boot — это секта. Нет, это просто неправильный корень проекта.
3. Проверяем Wrapper и Java: ./gradlew --version
После импорта очень хочется сразу жать bootRun, но полезно сделать один маленький, почти ритуальный шаг: убедиться, что Gradle запускается через Wrapper и что он видит нормальную Java. Это как перед поездкой проверить, что у машины есть бензин, а колёса не квадратные. В мире разработки такой чек экономит часы: вы сразу понимаете, где проблема — в коде или в окружении.
Команда проверки очень простая. В терминале откройте корень проекта (там, где gradlew) и выполните:
# Проверяем, что запускается именно Wrapper и какая JVM видна Gradle
./gradlew --version
На Windows можно вызвать так:
gradlew.bat --version
Вывод будет примерно такого вида (цифры могут отличаться — и это нормально):
Gradle 9.4
------------------------------------------------------------
Build time: ...
Kotlin: ...
Groovy: ...
Ant: ...
JVM: 25 (Vendor ...)
OS: ...
Здесь важны не все строки подряд, а два факта: Gradle запускается, и JVM примерно та, которую вы ожидаете увидеть (в нашем baseline это Java 25). Если команда сработала — отлично. Если первый запуск идёт долго, это тоже нормально: Gradle может скачивать дистрибутив, зависимости и докладывать что-то в кеш. Это не «тормозной компьютер», а «проект впервые оживает».
Ещё один практический нюанс: команды с Wrapper всегда нужно запускать из корня проекта. Если вы случайно убежали в src/main/java, то ./gradlew либо не найдётся, либо Gradle не увидит нужный контекст сборки. В этот момент начинающий разработчик обычно торжественно объявляет: «У меня не работает Spring». На деле не работает просто текущая директория.
4. ./gradlew tasks: карта возможностей проекта
Когда проект импортирован, а Gradle запускается, следующий вопрос звучит очень инженерно: «Что этот проект вообще умеет делать?» В Gradle это формулируется как «какие задачи (tasks) доступны». Хорошая новость: вам не нужно угадывать команды на память. Плохая новость: задач будет много, и поначалу это выглядит как список заклинаний.
Команда, которая печатает список задач, выглядит так:
# Смотрим, какие задачи вообще доступны в проекте (включая bootRun)
./gradlew tasks
Вы получите большой вывод, где задачи обычно сгруппированы по разделам. Нас сейчас интересует не весь каталог, а несколько опорных точек, по которым удобно ориентироваться в проекте. Например, вы почти наверняка увидите задачи вроде clean, build, test. А если подключён Spring Boot plugin (у нас он подключён), то появятся и Boot-задачи, в том числе bootRun.
Полезный трюк для новичка — не просто смотреть список, а спросить у Gradle: «А что конкретно делает вот эта задача?» Для этого есть help по задаче:
# Получаем описание конкретной задачи: что делает и какие у неё опции
./gradlew help --task bootRun
Вывод обычно короткий и понятный: описание и параметры. Это хороший способ не превращать работу с Gradle в гадание.
Чтобы закрепить «карту задач» в голове, удобно иметь маленькую таблицу. Не как «надо выучить», а как «когда я растерялся, я смотрю сюда»:
| Команда | Что делает (человечески) | Что вы ожидаете увидеть |
|---|---|---|
| ./gradlew tasks | Печатает список доступных операций проекта | Большой список задач, среди них bootRun |
| ./gradlew help --task bootRun | Объясняет конкретную задачу | Короткое описание, что bootRun запускает Boot-приложение |
| ./gradlew test | Запускает тесты (пока просто «проверка, что всё живо») | Итог BUILD SUCCESSFUL или понятная ошибка |
И важная мысль: Gradle task — это не «просто команда». Это операция проекта, которая знает зависимости. Например, если для bootRun нужно сначала скомпилировать код, Gradle сам это сделает. Вам не надо руками выполнять «compile, потом run, потом ещё что-то». В этом и есть один из смыслов build tool.
5. ./gradlew dependencies: дерево зависимостей
После задач логично посмотреть на вторую большую часть реальности проекта: зависимости. Новичку часто кажется, что «у меня же в dependencies всего две строки». Но через стартеры и транзитивные зависимости внутрь проекта заходит довольно много библиотек. И это не «ужас-ужас», а нормальная взрослая жизнь Java-сервиса: вы подключаете не одну баночку йогурта, а целый набор ингредиентов.
Самая простая команда:
# Печатаем дерево зависимостей (может быть большим — это нормально)
./gradlew dependencies
Она распечатает дерево зависимостей. И да, оно может быть длинным. Пугаться не нужно. В этом дне мы не разбираем версионные войны и конфликты — нам важно научиться видеть картину зависимостей текстом, без IDE, потому что это один из самых универсальных способов диагностики.
Чтобы вывод было проще читать, полезно указывать конкретную конфигурацию. Самая практичная для вопроса «что реально нужно приложению при запуске» — runtimeClasspath:
# Смотрим зависимости, которые реально попадают в classpath приложения при запуске
./gradlew dependencies --configuration runtimeClasspath
Вы увидите дерево, в котором где-то наверху будет ваш starter, например spring-boot-starter-webmvc (если вы его выбрали), а ниже — целая ветка библиотек. Это ровно тот момент, когда вы начинаете понимать: starter — это «входная точка», а не «один jar».
Чтобы было проще сопоставить это с реальным проектом, держите в голове, что список зависимостей «живёт» в build.gradle.kts. Примерно так (это не новый материал, просто привязка к месту в файле):
dependencies {
// Зависимости для основного кода приложения
implementation("org.springframework.boot:spring-boot-starter-webmvc")
// Зависимости только для тестов (в прод не попадают)
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
Сейчас нам достаточно двух навыков: уметь запустить команду и уметь себя успокоить фразой «длинный список — это нормально, проект не сломан». А вот разбираться, почему там именно такие ветки, мы сегодня не будем — это легко превращается в «а давайте ещё два часа посмотрим на дерево».
6. ./gradlew bootRun: запуск и остановка
Вот мы и дошли до кульминации дня: запуска проекта так, чтобы он был частью рабочего цикла сборки, а не «настроением вашей IDE». bootRun — это Gradle-задача от Spring Boot plugin, которая поднимает приложение прямо из проекта. С точки зрения новичка это выглядит как «я написал одну команду — и у меня стартовал сервер». С точки зрения инженера — это запуск, который использует правильный classpath и встроен в модель сборки.
Запускаем из корня проекта:
# Запуск приложения через Gradle (правильный classpath и модель сборки)
./gradlew bootRun
Первый запуск может занять больше времени: Gradle может докачивать зависимости, собирать классы, прогревать кеши. Это нормально. В консоли вы увидите знакомую Boot-картину: баннер, затем лог-строки старта и в какой-то момент — строку, по которой можно понять, что всё успешно.
Пример того, что вы ищете глазами (формулировки могут отличаться, но смысл один):
Tomcat started on port 8080 (http)
Started CatalogServiceApplication in 1.2 seconds
Обратите внимание: мы сейчас не анализируем, «почему Tomcat» и «что именно произошло на старте». На этом этапе нам достаточно простого подтверждения: процесс живой, приложение не упало, порт поднялся.
Как проверить, что сервис действительно слушает порт? Самый простой способ — открыть браузер и сходить на:
http://localhost:8080/
Поскольку мы пока не писали никаких контроллеров и страниц, вы, скорее всего, увидите 404. И это как раз хорошая новость: сервер поднялся и честно сказал «такого URL я не знаю». Для тех, кто любит терминал, можно сделать «проверку без браузера»:
curl -i http://localhost:8080/
Ожидаемый смысл ответа — статус 404, что-то вроде:
HTTP/1.1 404
...
И ещё один важный жизненный навык: как остановить bootRun. В терминале это делается классически:
Ctrl + C
Когда вы остановили приложение, порт 8080 освобождается, и следующий запуск снова сможет стартовать. Если вы случайно оставили процесс висеть, а потом пытаетесь запустить второй раз, можно получить ошибку «порт занят». Это не «сломался Spring», а просто «у вас уже есть запущенный процесс».
7. IDE и терминал: два пульта управления одним проектом
В идеальном мире терминал и IDE не конкурируют, а дополняют друг друга. IDE удобна, потому что показывает подсветку, навигацию по коду, кнопки запуска и вообще создаёт ощущение контроля. Терминал удобен, потому что честно и одинаково работает на любой машине, в любой IDE и в любой среде. И если вы научились делать базовые действия через ./gradlew, вы перестаёте быть заложником конкретной кнопки.
Практически это означает вот что: даже если вы запускаете bootRun из IDE через Gradle tool window, полезно знать, что в терминале это одна команда. А ещё полезно понимать, что если IDE «зависла на синхронизации», вы можете в терминале выполнить ./gradlew tasks и увидеть, что проект в принципе живой, просто IDE сейчас переваривает модель.
Ещё один спокойный критерий «всё в порядке»: когда вы делаете одно и то же действие в IDE и в терминале, результат должен быть одинаковым. Если из IDE проект стартует, а из терминала нет (или наоборот), это почти всегда сигнал про разные JDK, разные рабочие директории, разные настройки Gradle (например, IDE использует не Wrapper). И это как раз то место, где вы уже не новичок «на удачу», а человек, который умеет диагностировать несостыковки.
Если хотите сделать для себя маленькую «шпаргалку на ладони», то она будет почти смешной по размеру:
Увидеть задачи: ./gradlew tasks
Увидеть зависимости: ./gradlew dependencies
Запустить приложение: ./gradlew bootRun
Вот это и есть минимальный рабочий цикл, который не зависит от настроения вашей IDE.
8. Типичные ошибки при работе с Gradle и IDE
Ошибка №1: проект открыт как «папка», а не импортирован как Gradle-проект.
Это проявляется очень по-разному: у кого-то IDE не видит зависимости, у кого-то нет Gradle tool window, у кого-то «красный» код и отсутствуют классы Spring. Часто выглядит так, будто проект «не компилируется», хотя в терминале ./gradlew bootRun работает. Лечится просто: импортируете проект именно как Gradle-проект и включаете использование Wrapper.
Ошибка №2: команды запускаются не из корня проекта.
Вы заходите в терминал, на автомате выполняете ./gradlew bootRun — и получаете «No such file or directory» или Gradle ругается, что не видит build.gradle.kts. Это не загадка и не проклятие, а обычная файловая система. Проверьте, что вы находитесь в папке, где лежат gradlew и build.gradle.kts.
Ошибка №3: запускается глобальный Gradle вместо Wrapper.
Иногда в системе установлен Gradle, и рука тянется написать gradle bootRun. Это может работать… до первого несовпадения версии или настроек. В учебном и командном проекте лучше сразу выработать привычку: всегда ./gradlew .... Wrapper — это способ сказать: «мы собираем этим инструментом и этой версией», а не «у кого что установлено».
Ошибка №4: несостыковка Java в IDE и Java в Gradle Toolchain.
Сборка через терминал может использовать нужную Java (toolchain), а IDE анализирует проект на другой версии и начинает ругаться странными ошибками. Или наоборот: IDE запускает на одном JDK, а терминал — на другом, и поведение расходится. Узнаётся это по простому симптому: «в IDE работает, а ./gradlew нет» или наоборот. Начните диагностику с ./gradlew --version и настроек JDK в IDE.
Ошибка №5: первый запуск «долго висит», и кажется, что всё сломалось.
На первом старте Gradle скачивает зависимости, и это может занять минуты (особенно на медленном интернете). В этот момент важно не делать резких движений вроде «удалю папку .gradle», «перегенерю проект», «переустановлю Java». Дайте процессу закончить работу. Если загрузка действительно зависла, вы хотя бы будете видеть, на каком шаге, и сможете спокойно разбираться.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ