JavaRush /Курсы /Spring Boot /Starter как набор зависимостей

Starter как набор зависимостей

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

1. Ручная сборка стека — плохой старт

Если раньше вы писали в основном консольные Java-приложения, зависимость обычно выглядела просто: «мне нужна библиотека — я её подключил». С web-приложениями, даже маленькими, это быстро перестаёт работать: для одной «фичи» вам нужны десятки jar-ов, причём часть из них нужна не вам напрямую, а инфраструктуре. И вот вы уже не пишете код, а собираете лего без картинки на коробке.

Представьте, что вам нужно «просто поднять HTTP-сервис и отдавать JSON». Вручную это значит выбрать web-фреймворк, сервер (Tomcat/Jetty/Undertow), JSON-библиотеку, логирование, набор базовых Spring-модулей и ещё аккуратно согласовать версии так, чтобы всё это вообще дружило. В этот момент многие новички начинают думать, что «Spring сложный». Хотя усложняется здесь не программирование, а ручная сборка стека.

Spring Boot решает эту боль не тем, что «прячет зависимости», а тем, что даёт удобные и осмысленные точки входа. Первая такая точка входа — это starter.

2. Что такое starter в Spring Boot

Слово starter в названии зависимости звучит так, будто вам выдают «готовое приложение в коробке». На деле всё проще и надёжнее: starter — это зависимость, которая тянет за собой набор других зависимостей, подобранных под конкретный сценарий. Обычно сам starter почти не содержит прикладного кода. Его задача — сказать сборке: «мне нужен вот такой стек».

В Gradle это выглядит максимально буднично — как обычная строка в dependencies:

dependencies {
    // Подключаем не “одну библиотеку”, а целый сценарий (web MVC на servlet-стеке)
    implementation("org.springframework.boot:spring-boot-starter-webmvc")
}

И здесь происходит важный психологический поворот. Вы не «подключили одну библиотеку webmvc». Вы объявили намерение: «моё приложение будет servlet-based MVC web-приложением на Spring». В ответ Gradle скачает не один артефакт, а целый граф транзитивных зависимостей. Пока нам достаточно просто держать в голове: за одной строкой скрывается целая ветка библиотек.

Здесь легко перепутать starter и BOM из прошлой лекции. BOM отвечает на вопрос «какие версии совместимы», а starter — на вопрос «какие библиотеки обычно нужны для этого сценария». Это разные роли, и вместе они дают очень мощный эффект: вы выбираете сценарий одной строкой и получаете согласованный набор библиотек без ручного подбора версий.

3. Curated dependency set и «комплект деталей»

Слово curated в IT обычно означает «подобранное и проверенное». В нашем контексте curated dependency set — это набор зависимостей, который уже собрали за вас так, чтобы он был логичным и в большинстве проектов работал «по умолчанию». Это похоже на комплект для сборки стола: вам не нужно отдельно покупать 32 шурупа, 4 ножки и шестигранник — вы берёте коробку, где всё это уже есть и подходит друг к другу.

Если перевести это на язык Spring Boot-проектов, то starter — это «коробка под сценарий». Несколько примеров сценариев, которые мы будем встречать в catalog-service по мере роста проекта, выглядят так:

Сценарий (человеческим языком) Как это выражается в Gradle Что вы на самом деле “просите” у платформы
«Мне нужен web-сервис на MVC» spring-boot-starter-webmvc MVC, JSON-конвертация, встроенный сервер и базовая web-инфраструктура
«Мне нужна диагностика в runtime» spring-boot-starter-actuator Actuator-эндпоинты, интеграции для health/info и диагностический каркас
«Мне нужен нормальный test-стек» spring-boot-starter-test JUnit 6 + Spring Test + удобные тестовые утилиты в согласованных версиях

Заметьте: в этих строках нет перечисления низкоуровневых модулей. Это нормально. Хороший build.gradle.kts — это не список всех болтов, а список того, какие возможности нужны проекту.

И ещё одно: starter — это не «магия». Это просто зависимость, которая раскрывается в другие зависимости. Магия начинается только тогда, когда вы перестаёте смотреть, что именно подключили, и ждёте, что оно «само всё придумает». Мы так делать не будем: мы будем понимать, что происходит, просто на понятном уровне.

4. Starter и поведение приложения

Когда новички слышат «подключил starter — и всё заработало», кажется, будто тут есть какая-то мистика. На самом деле логика простая: starter приносит на classpath нужный набор библиотек, а Spring Boot уже умеет поднять разумную инфраструктуру поверх такого classpath. То есть starter не «включает фичу» волшебной кнопкой, а меняет доступный стек приложения.

Например, spring-boot-starter-webmvc — это не «один jar с web-кодом», а выбранный web-сценарий на servlet-стеке. Поэтому вместе с ним в проект приезжают Spring MVC-модули, поддержка JSON, встроенный сервер и другие части, без которых такой сервис обычно не живёт.

Пока здесь достаточно удержать одну мысль: starter влияет на поведение через classpath. А как этот граф реально читать, откуда именно берутся Tomcat, Jackson и другие библиотеки, уже выясняют через dependency tree.

5. Прямые зависимости в catalog-service

catalog-service в этом курсе — учебный проект, но он должен быть похож на нормальный, живой сервис. И одна из главных привычек, которые стоит вырастить рано: не превращать build.gradle.kts в склад случайных jar-ов. Список прямых зависимостей должен быть коротким, потому что каждая строка — это решение, которое меняет classpath и поведение приложения.

Если на минуту отвлечься от текущего catalog-service и взять абстрактный минимальный Boot-пример, набор прямых зависимостей может выглядеть так. Это именно минимальный пример, а не текущий snapshot дня:

dependencies {
    // Базовый каркас Spring Boot (без привязки к web/DB и т.п.)
    implementation("org.springframework.boot:spring-boot-starter")

    // Тестовый “комплект”: JUnit + Spring Test + утилиты
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Такой пример полезен именно как абстракция: видно, что прямых зависимостей мало, и каждая строка выражает роль, а не внутренности classpath.

Рабочий snapshot catalog-service, от которого мы сейчас отталкиваемся, уже конкретнее: сервис собран через web-сценарий, а не через базовый starter. Его ядро — spring-boot-starter-webmvc плюс spring-boot-starter-test. Если позже поверх этого snapshot понадобится ещё и диагностический слой, build дорастает буквально на одну понятную строку:

dependencies {
    // Web MVC на servlet-стеке: контроллеры, JSON, embedded server и базовая web-инфраструктура
    implementation("org.springframework.boot:spring-boot-starter-webmvc")

    // Диагностика: health/info/metrics и прочие actuator-возможности
    implementation("org.springframework.boot:spring-boot-starter-actuator")

    // Тестовый starter — обычно остаётся один, без ручной сборки тест-стека
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Это уже расширение текущего baseline, а не его скрытая замена. Даже с Actuator build по-прежнему читается как список возможностей: web, диагностика, тесты. Мы не перечисляем вручную Tomcat, Jackson и logback — это делает выбранный набор starter’ов.

Сравните это с «ручным» стилем, который часто встречается у новичков после пары гугл-запросов. Он выглядит «серьёзно», но на практике почти всегда хуже:

dependencies {
    // “Ручной” набор модулей под web-слой: выглядит контролируемо, но легко промахнуться и получить конфликт версий
    implementation("org.springframework:spring-webmvc")
    implementation("org.apache.tomcat.embed:tomcat-embed-core")
    implementation("com.fasterxml.jackson.core:jackson-databind")
    implementation("ch.qos.logback:logback-classic")
    implementation("org.slf4j:slf4j-api")
}

Проблема здесь не в том, что эти библиотеки «плохие». Проблема в другом: вы взяли на себя роль команды Spring Boot. Теперь вы отвечаете за совместимость, за правильный набор модулей, за выравнивание версий и за то, чтобы ничего не забыть. Для учебного проекта это гарантированная когнитивная перегрузка. Для продакшена такой путь иногда оправдан, но только если вы уже хорошо понимаете, что именно делаете и зачем.

В рамках курса мы хотим, чтобы build.gradle.kts был похож на список того, что нужно проекту, — web, actuator, tests, — а не на перечень всех деталей внутреннего устройства.

6. Стартеры и обычные зависимости

Очень легко уехать в крайность: «раз в Boot есть starter’ы, значит всё подключается starter’ами». Это не так. Starter — это хороший формат для сценариев. Но иногда вам нужен не сценарий, а небольшой инструмент. Например, аннотационный процессор для генерации метаданных конфигурации — это не runtime-фича, а build-time поддержка. И тут starter не нужен.

В catalog-service у нас будет зависимость, которую часто путают со starter’ом только потому, что она тоже из Boot. Но роль у неё другая: spring-boot-configuration-processor. Она помогает IDE и сборке понимать ваши кастомные @ConfigurationProperties и подключается как annotation processor.

В Gradle это обычно выглядит так:

dependencies {
    // Нужен только на этапе компиляции: генерирует метаданные для IDE/подсказок по @ConfigurationProperties
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")

    // Тестовый стек — отдельно, чтобы не утяжелять runtime classpath
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

И вот здесь появляется ещё одна важная мысль для новичка: не каждая зависимость должна лежать в implementation. Иногда это testImplementation — нужно только для тестов, иногда это annotationProcessor — нужно на этапе компиляции, иногда это developmentOnly — нужно для локальной разработки. Если всё подряд складывать в implementation, classpath становится тяжелее, шумнее и менее предсказуемым.

Starter’ы помогают держать основной стек в порядке, но не отменяют здравый смысл. Просто теперь базовый вопрос звучит иначе: «мне нужен сценарий — starter, или инструмент — обычная библиотека/процессор?»

7. Типичные ошибки при работе со starter’ами

С starter’ами чаще всего ошибаются не потому, что они сложные, а потому что они ломают привычную модель «одна строка = одна библиотека». На первых порах это как пересесть с велосипеда на электросамокат: вроде всё то же самое, но внезапно быстрее, а значит и в стену въехать проще. Ниже — несколько ошибок, которые стоит заранее распознавать, чтобы не превращать dependency management в лотерею.

Ошибка №1: думать, что starter — это «один жирный jar со всем внутри».
Starter — это не «fat jar» и не какая-то монолитная библиотека. Он раскрывается в набор зависимостей, и именно этот набор формирует classpath. Если держать в голове модель «там внутри магия», вы неизбежно перестанете понимать, что реально подключено и почему приложение ведёт себя именно так.

Ошибка №2: подключить starter и рядом вручную “досыпать” низкоуровневые модули того же сценария.
Например, добавить spring-boot-starter-webmvc, а рядом ещё spring-webmvc, tomcat-embed-core и пару Jackson-модулей «для надёжности». Выглядит как забота, но на деле это дублирование и потенциальные конфликты. Если у вас уже есть starter, сначала считайте, что он привёз базовый набор, и только потом добавляйте то, чего действительно не хватает — и что вы можете объяснить.

Ошибка №3: выбирать starter по “похожему названию”, а не по задаче.
В экосистеме Boot есть зависимости, которые звучат почти одинаково. Новичок часто выбирает по принципу «в названии есть web — значит оно». Правильнее сначала сформулировать сценарий словами: «я хочу servlet MVC» или «я хочу диагностику», — и уже под него выбирать starter. Название — это подсказка, но не гарантия попадания в цель.

Ошибка №4: ожидать, что starter решит архитектуру приложения.
Starter решает проблему слоя зависимостей: он приносит согласованный набор библиотек. Он не решает, как вам организовать пакеты, где держать бизнес-логику, как назвать сервисы и какие endpoint’ы делать. Если вы ждёте, что «подключил starter — и всё стало правильно», вы очень быстро начнёте строить приложение по принципу «ну Boot же умный».

Ошибка №5: пытаться «оптимизировать» build и удалить “лишние” транзитивные зависимости, не понимая их роли.
Когда вы впервые увидите дерево зависимостей, оно покажется огромным. И это нормальная реакция. Но желание «почистить» граф без понимания быстро приводит к ситуации, когда приложение перестаёт стартовать, а причина — в одной случайно исключённой библиотеке. Starter’ы существуют как раз потому, что в сложном стеке «просто убрать лишнее» обычно нельзя без последствий.

1
Задача
Spring Boot, 4 уровень, 1 лекция
Недоступна
Один starter вместо ручного web-набора
Один starter вместо ручного web-набора
1
Задача
Spring Boot, 4 уровень, 1 лекция
Недоступна
Короткий build с двумя runtime-сценариями
Короткий build с двумя runtime-сценариями
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ