Maven, зависимости и стартеры

Стартеры Spring Boot — это «комплекты под задачу»: одна зависимость тянет за собой согласованный набор библиотек нужных версий.
Суть: вместо ручного подбора десятка библиотек вы подключаете один стартер. Parent POM и BOM гарантируют, что версии не конфликтуют.

Управление зависимостями в Java — историческая боль. Библиотека A требует версию 2 библиотеки X, библиотека B — версию 3, и приложение падает с загадочной ошибкой. Эта проблема называется «dependency hell». Spring Boot решает её через две вещи: стартеры и управление версиями.

Что такое стартер

Стартер — это специальная зависимость, которая сама по себе почти не содержит кода, но тянет за собой набор других библиотек под конкретную задачу. Например, spring-boot-starter-web приносит Spring MVC, встроенный Tomcat, Jackson для JSON и валидацию. Вам не нужно знать названия и версии всех этих библиотек — стартер собрал их за вас.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
</dependencies>

Заметьте: у зависимостей нет тега <version>. Откуда же Maven знает версию? Из parent POM.

Как работает под капотом: parent POM и BOM

В каждом проекте Spring Boot есть строка с родителем:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.0</version>
</parent>

Этот spring-boot-starter-parent содержит BOM (Bill of Materials — «ведомость материалов») — большую таблицу, где для сотен библиотек прописаны проверенные, совместимые версии. Когда вы подключаете стартер без версии, Maven берёт её из BOM. Так гарантируется, что Jackson, Hibernate, Tomcat и всё остальное собраны в комбинации, которую команда Spring протестировала.

  spring-boot-starter-parent  (версия 3.3.0)
        |
        +-- BOM: таблица версий
        |       jackson = 2.17.x
        |       hibernate = 6.5.x
        |       tomcat = 10.1.x
        |       ... сотни библиотек
        v
  Ваш проект подключает стартеры БЕЗ версий
        |
        +-- starter-web   --> tomcat, jackson, spring-mvc
        +-- starter-data-jpa --> hibernate, spring-data
        v
  Maven берёт версии из BOM => конфликтов нет

Смоделируем механику разрешения версий на простом примере. По сути BOM — это словарь «библиотека → версия», а стартеры просто перечисляют, что им нужно:

# BOM: согласованная таблица версий
bom = {
    "jackson": "2.17.1",
    "hibernate": "6.5.2",
    "tomcat": "10.1.24",
    "spring-mvc": "6.1.8",
}

# Стартеры перечисляют нужные библиотеки (без версий)
starters = {
    "starter-web": ["tomcat", "jackson", "spring-mvc"],
    "starter-data-jpa": ["hibernate"],
}

def resolve(selected_starters):
    libs = {}
    for st in selected_starters:
        for lib in starters[st]:
            libs[lib] = bom[lib]   # версию берём из BOM
    return libs

print(resolve(["starter-web", "starter-data-jpa"]))
# Все версии согласованы между собой

Нажмите «Попробуй сам ▶» — увидите, как «стартеры» разворачиваются в конкретные библиотеки с версиями из единой ведомости.

Частые ошибки

  • Указывать версию вручную. Прописав свою версию у управляемой стартером библиотеки, вы рискуете сломать совместимость, заданную BOM.
  • Тянуть библиотеку напрямую вместо стартера. Лучше искать готовый стартер — он соберёт зависимости правильно.
  • Менять версию Boot, не проверив changelog. Между минорными версиями (3.2 → 3.3) меняются дефолты — читайте release notes.

Best practices

  • Подключайте стартеры без версий — пусть BOM управляет версиями.
  • Версию Boot держите в <parent> или в свойстве, чтобы менять её в одном месте.
  • Для апгрейда используйте инструменты вроде OpenRewrite — они автоматизируют миграцию.

Итог: стартеры избавляют от ручного подбора библиотек, а parent POM с BOM гарантирует совместимость версий. Это и есть «магия», благодаря которой проект Spring Boot собирается без dependency hell.

Проверьте себя
1. Что делает spring-boot-starter-web при подключении в проект?
AСоздаёт базу данных
BТянет за собой согласованный набор: Spring MVC, встроенный Tomcat, Jackson и валидацию
CГенерирует фронтенд-код на JavaScript
DЗаменяет Maven на Gradle
2. Откуда Maven берёт версии библиотек, если в зависимости-стартере версия не указана?
AИз последней версии в интернете
BИз BOM в spring-boot-starter-parent — таблицы согласованных версий
CВерсия выбирается случайно
DИз переменных окружения