JavaRush /Курсы /Spring Boot /Initializr: структур...

Initializr: структура и starter-зависимости

Spring Boot
3 уровень , 3 лекция
Открыта

1. Смысл разборки skeleton до кода

Когда новичок впервые открывает Spring Boot-проект, он чаще видит не «аккуратный каркас», а «лес папок». Это нормальная реакция: мозг честно признаёт, что пока не умеет отличать важное от второстепенного. В этой лекции мы сделаем проект читаемым, чтобы вы перестали бояться структуры и начали использовать её как подсказку, а не как испытание на веру.

После build.gradle.kts картина всё ещё неполная: правила сборки уже понятны, но пока не видно, во что они превращаются на диске. Поэтому сейчас буквально «раскроем коробку» сгенерированного каркаса: папки, классы, ресурсы и стартеры.

Самая полезная мысль: Initializr генерирует проект не потому, что «так принято», а потому, что Boot и Gradle опираются на конвенции. Если проект соответствует этим конвенциям, инструменты понимают, где искать код, где искать ресурсы, где лежат тесты, как запускать сборку и как упаковывать результат.

Удобно держать в голове простую схему: в любом нормальном Gradle-проекте (и Boot-проекте тоже) файлы делятся на несколько зон ответственности.

flowchart TD
  A[Проект catalog-service] --> B[Build-инфраструктура]
  A --> C[Код приложения]
  A --> D[Ресурсы]
  A --> E[Тесты]

  B --> B1[gradlew / gradle/]
  B --> B2[build.gradle.kts / settings.gradle.kts]
  C --> C1[src/main/java]
  D --> D1[src/main/resources]
  E --> E1[src/test/java]

Пока вы не научились «читать» эту карту, IDE выглядит как шкаф, где всё перемешано: носки лежат рядом с кастрюлями, а паспорт — между пакетами из супермаркета. Как только вы запомните зоны, проект превращается в понятную квартиру: кухня — на кухне, спальня — в спальне, а кот… ну, кот всё равно где-то на клавиатуре, но хотя бы вы знаете планировку.

2. Карта проекта: корень репозитория

Корень проекта — место, где у новичка обычно начинается паника: «Почему тут столько файлов, а я ещё ни строчки бизнес-кода не написал?» Спокойно: большинство файлов в корне — это не код приложения, а инструкция, как собирать и запускать проект. Это как коробка от техники: вы ещё не включили устройство, но уже есть паспорт, гарантия и зарядка.

В типичном проекте catalog-service, созданном через Initializr под Gradle, вы увидите примерно такую структуру:

catalog-service/
├─ build.gradle.kts
├─ settings.gradle.kts
├─ gradlew
├─ gradlew.bat
├─ gradle/
│  └─ wrapper/
│     ├─ gradle-wrapper.jar
│     └─ gradle-wrapper.properties
└─ src/
   ├─ main/
   └─ test/

Давайте разложим это по смыслу. Таблица ниже нужна не для того, чтобы «всё выучить», а чтобы вы привыкли: любой файл в корне играет свою роль.

Что вы видите Что это такое Почему это важно
build.gradle.kts главный build-скрипт Gradle описывает зависимости, плагины и сборку проекта
settings.gradle.kts настройки Gradle-проекта минимум: имя проекта (root project name)
gradlew /
gradlew.bat
Gradle Wrapper-скрипты запуск Gradle правильной версии из проекта
gradle/wrapper/* «начинка» Wrapper здесь зафиксирована версия Gradle и способ её скачать
src/ исходники это уже «территория приложения»: код, ресурсы, тесты

Важно психологически: корень проекта — это в первую очередь инфраструктура, а не бизнес-логика. Это не значит, что файлы «неважные». Это значит, что вы не обязаны прямо сейчас понимать их на уровне архитектора Gradle-плагинов. Достаточно понимать, что без них проект теряет воспроизводимость.

Для ориентира вот пример минимального settings.gradle.kts, который обычно генерируется:

// Имя корневого проекта: его будет показывать IDE, и оно часто используется в сборке
rootProject.name = "catalog-service"

Один файл, одна строчка — и всё равно полезно: из неё получится имя модуля в IDE и базовое имя артефакта (а ещё это просто приятно читается).

3. Папка src: главный разделитель «код / ресурсы / тесты»

Папка src — это место, где начинающий программист обычно облегчённо вздыхает: «О, наконец-то что-то похоже на код». Но даже здесь важно не превращать проект в свалку. Главный смысл src в том, что Gradle и Java-проекты уже много лет договорились о стандартной раскладке: где лежит код, где лежат ресурсы, где лежат тесты. Эта договорённость экономит вам массу ручных настроек.

Внутри src вы почти всегда увидите два верхних уровня: main и test. Это не «каприз Spring», а общий инженерный стандарт: приложение и тесты разделяются физически.

src/
├─ main/
│  ├─ java/
│  └─ resources/
└─ test/
   ├─ java/
   └─ resources/

src/test/resources может появиться позже — когда тестам понадобятся файлы и данные (JSON, SQL-скрипты, фикстуры и т.д.).

Смысл такой: всё, что в src/main, относится к «продуктовому» приложению, то есть к тому, что в итоге запускается. Всё, что в src/test, относится к тестовому коду, то есть к тому, что помогает это приложение проверять.

Ещё одна полезная «приземлённая» мысль: Gradle не пытается угадывать, где у вас Java-код. Он просто смотрит в стандартные папки. Поэтому если вы вдруг положите Java-класс в src/main/resources, он не станет «ресурсом с интеллектом», а станет проблемой.

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

Путь Что там лежит Как думать
src/main/java исходники приложения (Java) «то, что компилируется»
src/main/resources ресурсы (не Java) «то, что кладётся рядом с приложением»
src/test/java тесты на Java «то, что проверяет приложение»
src/test/resources ресурсы для тестов «данные/файлы, нужные тестам»

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

4. src/main/java: базовый пакет и главный класс приложения

src/main/java — самая «живая» часть проекта: здесь будет ваш прикладной код. Но и тут Initializr не создаёт хаос, а сразу задаёт правильный старт: создаёт базовый пакет и главный класс. На этом этапе особенно важно понять, что пакет в Java — это не просто строка в package ...;, а ещё и структура папок, и «границы видимости» для сканирования компонентов.

Если вы выбрали, например, group = com.example и artifact = catalog-service, то Initializr обычно собирает базовый пакет как:

com.example + catalogservice  →  com.example.catalogservice

И тогда путь до главного класса будет таким:

src/main/java/com/example/catalogservice/CatalogServiceApplication.java

Внутри — совсем минимальный Boot entry point: класс с main() и аннотацией, которая говорит Boot: «Вот отсюда начинаем».

package com.example.catalogservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Главный класс приложения: именно отсюда Spring Boot стартует и поднимает контекст
@SpringBootApplication
public class CatalogServiceApplication {

    public static void main(String[] args) {
        // Запуск Spring Boot-приложения (поднимается контекст, автонастройка, embedded-сервер и т.д.)
        SpringApplication.run(CatalogServiceApplication.class, args);
    }
}

Сейчас нам важно не «разобрать магию @SpringBootApplication по косточкам» (это будет отдельная тема курса), а увидеть назначение файла: это точка входа приложения, откуда оно стартует. В терминах обычной Java это похоже на главный метод консольной программы, только здесь он поднимает целый runtime и контейнер.

Ещё один важный момент из нашего повторения Spring Core: положение этого класса в пакете имеет значение. Очень грубо (и пока без деталей) можно думать так: Spring Boot смотрит на пакет главного класса и считает его «корнем» для поиска ваших компонентов. Поэтому хорошая привычка — держать главный класс в аккуратном верхнем пакете проекта, а не прятать его где-то в глубине вроде com.example.catalogservice.utils.startup.launcher.finalversion2.

Если вы потом добавите подпакеты (например, catalog, config и так далее), они логично окажутся «ниже» базового пакета, и проект будет естественно читаться.

5. src/test/java: тестовый каркас, который появляется сразу

Папка src/test/java у многих новичков вызывает удивление: «Я же ещё ничего не тестирую, почему мне уже выдали тест?» Это на самом деле очень правильный инженерный намёк от Initializr: хороший сервис — это не только код, но и минимальная проверка того, что он вообще способен стартовать. Даже если вы пока не готовы думать про тестовую стратегию, полезно знать, что каркас уже есть.

Initializr обычно создаёт тест в том же пакете, что и main-класс:

src/test/java/com/example/catalogservice/CatalogServiceApplicationTests.java

Вот типичный «пустой на вид» тест, который вам сгенерируют:

package com.example.catalogservice;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

// Интеграционный тест: поднимает Spring-контекст и проверяет, что приложение вообще стартует
@SpringBootTest
class CatalogServiceApplicationTests {

    @Test
    void contextLoads() {
        // Тело может быть пустым: если контекст не поднимется, тест упадёт ещё до выполнения этого метода
        // В курсе считаем, что это запускается на JUnit 6 (аннотация @Test по смыслу та же)
    }
}

С точки зрения новичка это выглядит почти как шутка: метод пустой, а файл есть. Но смысл как раз в том, что если контекст приложения не смог подняться, тест упадёт ещё до того, как начнёт выполнять ваш код внутри contextLoads(). То есть этот тест отвечает на вопрос: «Проект вообще живой? Он собирается? Он стартует хотя бы на уровне контекста?»

Мы сегодня не уходим в детали @SpringBootTest, JUnit 6 и тестовые подходы — просто фиксируем: тесты появляются не потому, что «надо по методичке», а потому, что проектный скелет должен иметь минимальный страховочный трос. Даже если вы пока не альпинист, трос уже пристёгнут — и это хорошо.

6. src/main/resources: ресурсы и дисциплина

src/main/resources — это место, где лежит всё, что нужно приложению, но не является Java-кодом. У начинающих тут две крайности: одни игнорируют эту папку («там какая-то магия»), другие начинают складывать туда всё подряд («ну это же просто файлы»). Реальность посередине: resources — это важный слой приложения, но со своей дисциплиной.

Типичный сгенерированный проект содержит конфигурационный файл приложения. В сгенерированном проекте вы можете увидеть здесь application.properties — это обычный вариант по умолчанию в Initializr, а не вторая равноправная линия проекта. В catalog-service мы держимся YAML-first, поэтому рабочим форматом считаем application.yaml, а не смесь двух форматов.

src/main/resources/
└─ application.yaml

Минимальное содержимое может выглядеть так:

spring:
  application:
    name: catalog-service

Если у вас после генерации лежит application.properties, это не проблема: важна сама папка resources и место, где живут настройки.

Идея папки resources в том, что её содержимое попадает в classpath приложения. Проще говоря, это файлы, которые приложение сможет «видеть» во время работы. Позже здесь могут лежать, например, статические файлы, конфиги, шаблоны, локализации — но сегодня важно просто научиться отличать код от ресурсов и не путать папки местами.

Самая частая ошибка новичка здесь выглядит так: он создаёт Java-класс в resources, потому что «в IDE так удобнее». Потом Gradle его не компилирует, Spring его не видит, а студент думает, что Spring сломался. На самом деле Spring не сломался — он просто не обязан искать Java-код там, где по стандарту лежат ресурсы.

7. Starter-зависимости в skeleton

После карты папок естественно возникает вопрос: почему этот каркас уже выглядит живым, хотя строк в dependencies мало? Ответ как раз в стартерах.

Когда вы впервые смотрите на build.gradle.kts, можно испытать странное чувство: «У меня всего две строки зависимостей, а проект выглядит как полноценный сервер. Где подвох?» Подвоха нет: это философия Spring Boot. Вместо того чтобы заставлять новичка собирать инфраструктуру по деталям, Boot предлагает вход через стартеры — готовые наборы зависимостей под типовой сценарий.

В форме Initializr такая зависимость может прятаться за именем уровня Spring Web, но в build.gradle.kts канонической строкой для курса остаётся org.springframework.boot:spring-boot-starter-webmvc.

dependencies {
    // Базовый web-starter курса: приносит Spring MVC, embedded-сервер и всё типовое для HTTP-приложения
    implementation("org.springframework.boot:spring-boot-starter-webmvc")

    // Тестовый starter: тестовая инфраструктура Spring + JUnit 6 
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Сейчас нам важно только понять смысл, не залезая в детали внутреннего состава. spring-boot-starter-webmvc — это «входной билет» в web-мир (серверная часть, MVC-инфраструктура и всё, что нужно, чтобы приложение вообще могло работать как HTTP-сервис). spring-boot-starter-test — аналогичный «входной билет» в тестовый мир (JUnit 6 и полезная тестовая инфраструктура).

Обратите внимание на психологически важную вещь: вы подключили стартер, а не «одну библиотеку». Поэтому и строк мало, и эффект большой. Это не магия, а заранее подобранный набор зависимостей. И это одна из причин, почему Boot-проекты так приятно стартуют: вы описываете сценарий («мне нужен web»), а платформа подтягивает базовый набор деталей.

Ещё вы можете заметить, что у зависимостей обычно не прописана версия. Это нормально для Boot-подхода: Initializr и Boot-плагин настроены так, чтобы проект держался на единой согласованной линии библиотек. Сегодня нам достаточно просто зафиксировать: «версия не всегда нужна в строке зависимости», и не пытаться лечить это добавлением случайных :1.2.3 «на всякий случай».

8. Мини-сверка: что уже готово

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

Если описать состояние catalog-service после Initializr одним абзацем, то получится так: в корне лежит инфраструктура сборки (Gradle и Wrapper), в src/main/java лежит точка входа приложения, в src/test/java лежит минимальный тест «контекст поднимается», а в src/main/resources есть место для ресурсов и конфигурации. Этого достаточно, чтобы проект синхронизировался с IDE, собирался и мог запускаться через стандартные Gradle-команды.

На этом этапе самая правильная стратегия — не «улучшать» структуру руками. Не нужно переносить файлы Wrapper, «потому что они мешают». Не нужно переименовывать пакеты в десяти местах, «потому что захотелось». Не нужно создавать папку my_code рядом с src, «потому что так проще». Каркас ценен тем, что он стандартный: стандартность — это ваша будущая экономия времени.

9. Типичные ошибки при разборе skeleton проекта

Ошибка №1: воспринимать сгенерированный проект как «набор мусора, который можно смело подчистить».
Часто рука тянется удалить gradlew, папку gradle/ и половину файлов в корне, потому что «они не похожи на код». Это ломает воспроизводимость сборки и превращает проект в «работает только у меня на ноутбуке». Если вы не уверены, зачем файл нужен, лучше сначала понять его роль, а не удалять.

Ошибка №2: путать src/main/java и src/main/resources.
Эта ошибка выглядит смешно ровно до момента, пока вы не потратите час на вопрос «почему Spring не видит мой класс», который лежит в resources. Java-код должен жить в src/main/java, ресурсы — в src/main/resources. Это не прихоть, это конвенция, на которой стоят Gradle и типичная Java-экосистема.

Ошибка №3: хаотично менять базовый пакет и имя главного класса.
Пакет главного класса — это не только «красота в имени», но и важная структурная точка проекта. Если вы переименовываете com.example.catalogservice в com.qwerty.myapp2.final, вы потом удивитесь, что всё вокруг тоже приходится переименовывать: папки, импорты, тесты. В учебном проекте лучше держать стабильные, читаемые имена и не устраивать ребрендинг каждые полчаса.

Ошибка №4: игнорировать тестовый каркас, потому что «я же не про тесты».
Тест contextLoads() выглядит пустым, и его хочется удалить как бесполезный. Но он служит первым сигналом: проект стартует как приложение, контекст собирается. Даже если вы пока не пишете тесты, иметь минимальную проверку — это как иметь аптечку в машине: пусть лучше лежит и не пригодится.

Ошибка №5: думать, что стартер — это «одна библиотека», и пытаться заменить его на ручной набор модулей.
Иногда новички видят «стартер» и решают: «О, я умный, сейчас заменю starter на три-четыре модуля, чтобы всё было под контролем». Обычно это заканчивается тем, что что-то не подтянулось, версии разъехались и проект перестаёт стартовать. Стартер — удобная входная точка. В рамках курса мы держимся starter-подхода, чтобы проект оставался простым и предсказуемым.

1
Задача
Spring Boot, 3 уровень, 3 лекция
Недоступна
Файлы на своих местах в generated skeleton
Файлы на своих местах в generated skeleton
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ