JavaRush /Курси /Spring Boot /Стартер як набір залежностей

Стартер як набір залежностей

Spring Boot
Рівень 4 , Лекція 1
Відкрита

1. Ручне збирання стека — поганий старт

Якщо раніше ви переважно писали консольні Java-застосунки, залежність зазвичай була простою: «мені потрібна бібліотека — я її підключив». А з web-застосунками, навіть невеликими, це швидко перестає працювати: для однієї «фічі» вам потрібні десятки JARів, причому частина з них потрібна не вам напряму, а інфраструктурі. І ось ви вже не пишете код, а збираєте конструктор LEGO без картинки на коробці.

Уявіть, що вам потрібно «просто підняти HTTP-сервіс і віддавати JSON». Ручний підхід означає вибрати web-фреймворк, сервер (Tomcat, Jetty або Undertow), JSON-бібліотеку, логування, набір базових Spring-модулів і ще акуратно узгодити версії так, щоб усе це взагалі дружило. У цей момент багато новачків починають думати, що «Spring складний». Хоча ускладнюється тут не програмування, а ручне збирання стека.

Spring Boot розв’язує цю проблему не тим, що «ховає залежності», а тим, що дає зручні й осмислені точки входу. Перша така точка входу — це starter.

2. Що таке starter у Spring Boot

Слово starter у назві залежності звучить так, ніби вам видають «готовий застосунок у коробці». Насправді все простіше й надійніше: стартер — це залежність, яка тягне за собою набір інших залежностей, підібраних під конкретний сценарій. Зазвичай сам стартер майже не містить прикладного коду. Його завдання — сказати збиранню: «мені потрібен ось такий стек».

У Gradle це виглядає максимально буденно — як звичайний рядок у dependencies:

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

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

Тут легко переплутати стартер і BOM з попередньої лекції. BOM відповідає на питання «які версії сумісні», а starter — на питання «які бібліотеки зазвичай потрібні для цього сценарію». Це різні ролі, і разом вони дають дуже потужний ефект: ви обираєте сценарій одним рядком і отримуєте узгоджений набір бібліотек без ручного підбору версій.

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

Слово curated в IT зазвичай означає «підібраний і перевірений». У нашому контексті curated dependency set — це набір залежностей, який уже зібрали за вас так, щоб він був логічним і в більшості проєктів працював «за замовчуванням». Це схоже на комплект для складання столу: вам не потрібно окремо купувати 32 шурупи, 4 ніжки і шестигранник — ви берете коробку, де все це вже є й підходить одне до одного.

Якщо перекласти це на мову Spring Boot-проєктів, то стартер — це «коробка під сценарій». Кілька прикладів сценаріїв, які ми ще побачимо в catalog-service у міру зростання проєкту, виглядають так:

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

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

І ще одне: стартер — це не «магія». Це просто залежність, яка розгортається в інші залежності. Магія починається лише тоді, коли ви перестаєте дивитися, що саме підключили, і чекаєте, що воно само все придумає. Ми так робити не будемо: ми будемо розуміти, що відбувається, просто на зрозумілому рівні.

4. Стартер і поведінка застосунку

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

Наприклад, spring-boot-starter-webmvc — це не «один JAR з web-кодом», а обраний web-сценарій на servlet-стеку. Тому разом із ним у проєкт приїжджають модулі Spring MVC, підтримка JSON, вбудований сервер та інші частини, без яких такий сервіс зазвичай не живе.

Поки що достатньо втримати одну думку: стартер впливає на поведінку через classpath. А як цей граф реально читати, звідки саме беруться Tomcat, Jackson та інші бібліотеки, уже з’ясовують через дерево залежностей.

5. Прямі залежності в catalog-service

catalog-service у цьому курсі — навчальний проєкт, але він має бути схожий на нормальний, живий сервіс. І одна з головних звичок, які варто виробити рано: не перетворювати build.gradle.kts на склад випадкових JARів. Список прямих залежностей має бути коротким, тому що кожен рядок — це рішення, яке змінює classpath і поведінку застосунку.

Якщо на хвилину відволіктися від поточного catalog-service і взяти абстрактний мінімальний Boot-приклад, набір прямих залежностей може виглядати так. Це саме мінімальний приклад, а не поточна базова конфігурація на сьогодні:

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-сценарій, а не через базовий стартер. Його ядро — spring-boot-starter-webmvc плюс spring-boot-starter-test. Якщо пізніше поверх цього snapshot знадобиться ще й діагностичний шар, збірка зростає буквально на один зрозумілий рядок:

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

    // Діагностика: health/info/metrics та інші можливості Actuator
    implementation("org.springframework.boot:spring-boot-starter-actuator")

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

Це вже розширення поточної базової конфігурації, а не її прихована заміна. Навіть із Actuator збірка і далі читається як список можливостей: web, діагностика, тести. Ми не перелічуємо вручну Tomcat, Jackson і logback — це робить обраний набір стартерів.

Порівняйте це з «ручним» стилем, який часто трапляється в новачків після кількох пошукових запитів. Він виглядає «серйозно», але на практиці майже завжди гірший:

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, тести, — а не на перелік усіх деталей внутрішньої будови.

6. Стартери й звичайні залежності

Дуже легко піти в крайність: «раз у Boot є стартери, значить усе підключається стартерами». Це не так. Стартер — це хороший формат для сценаріїв. Але іноді вам потрібен не сценарій, а невеликий інструмент. Наприклад, процесор анотацій для генерування метаданих конфігурації — це не runtime-фіча, а підтримка на етапі збирання. І тут стартер не потрібен.

У catalog-service у нас буде залежність, яку часто плутають зі стартером лише тому, що вона теж із Boot. Але роль у неї інша: spring-boot-configuration-processor. Вона допомагає IDE і збірці розуміти ваші власні @ConfigurationProperties і підключається як процесор анотацій.

У 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 стає важчим, шумнішим і менш передбачуваним.

Стартери допомагають тримати основний стек у порядку, але не скасовують здоровий глузд. Просто тепер базове питання звучить інакше: «мені потрібен сценарій — стартер, чи інструмент — звичайна бібліотека або процесор?»

7. Типові помилки під час роботи зі стартерами

Із стартерами найчастіше помиляються не тому, що вони складні, а тому, що вони ламають звичну модель «один рядок = одна бібліотека». На перших порах це як пересісти з велосипеда на електросамокат: начебто все те саме, але раптом швидше, а отже і в стіну в’їхати легше. Нижче — кілька помилок, які варто заздалегідь упізнавати, щоб не перетворювати керування залежностями на лотерею.

Помилка №1: думати, що стартер — це «один великий JAR з усім усередині».
Стартер — це не «fat jar» і не якась монолітна бібліотека. Він розкривається в набір залежностей, і саме цей набір формує classpath. Якщо тримати в голові модель «там усередині магія», ви неминуче перестанете розуміти, що реально підключено і чому застосунок поводиться саме так.

Помилка №2: підключити стартер і поруч вручну «досипати» низькорівневі модулі того самого сценарію.
Наприклад, додати spring-boot-starter-webmvc, а поруч ще spring-webmvc, tomcat-embed-core і пару Jackson-модулів «для надійності». Виглядає як турбота, але насправді це дублювання і потенційні конфлікти. Якщо у вас уже є стартер, спочатку вважайте, що він привіз базовий набір, і лише потім додавайте те, чого справді не вистачає — і що ви можете пояснити.

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

Помилка №4: чекати, що стартер розв’яже архітектуру застосунку.
Стартер розв’язує проблему шару залежностей: він приносить узгоджений набір бібліотек. Він не вирішує, як вам організувати пакети, де тримати бізнес-логіку, як назвати сервіси і які end-pointʼи робити. Якщо ви чекаєте, що «підключив стартер — і все стало правильно», ви дуже швидко почнете будувати застосунок за принципом «ну Boot же розумний».

Помилка №5: намагатися «оптимізувати» збірку і видалити «зайві» транзитивні залежності, не розуміючи їхньої ролі.
Коли ви вперше побачите дерево залежностей, воно здасться величезним. І це нормальна реакція. Але бажання «почистити» граф без розуміння швидко призводить до ситуації, коли застосунок перестає стартувати, а причина — в одній випадково виключеній бібліотеці. Стартери існують якраз тому, що в складному стеці «просто прибрати зайве» зазвичай не можна без наслідків.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ