JavaRush /Курсы /Spring Boot /Web-вход: spring-boot-star...

Web-вход: spring-boot-starter-webmvc

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

1. Web-вход и роль web-starter

Слово «сервис» звучит солидно. Кажется, будто достаточно создать проект catalog-service, добавить пару классов и гордо сказать: «У меня сервис». Но если к нему нельзя обратиться извне (например, через браузер или curl), то по факту это всё ещё внутренняя библиотека, которая живёт только в вашей IDE. Она может быть очень умной — но снаружи её никто не увидит.

Для backend‑разработчика «жить» обычно означает две вещи: процесс запускается как приложение и слушает входящие запросы. Пока нет web‑входа, вы не можете проверить контракт руками, не можете дать ссылку коллеге, не можете даже банально открыть http://localhost:... и увидеть реакцию приложения. Это немного похоже на кафе, где идеальный кофе, классная музыка и дружелюбный бариста… но вход замурован. Бариста счастлив, пользователи — нет.

Важно понять одну штуку: web‑вход в Boot появляется не потому, что вы «написали controller». Controller — это всего лишь правило «если пришёл запрос по такому пути — вызвать такой метод». Но чтобы запрос вообще мог прийти, приложению нужен web‑runtime: встроенный сервер, инфраструктура MVC и базовые компоненты, которые умеют принимать HTTP и сопоставлять его вашему коду.

Очень удобно, что Spring Boot не заставляет вас собирать это вручную. Он предлагает ровно один инженерный ход: подключить правильный starter. И именно это — наш первый шаг к web‑сервису.

2. Базовый стек курса: spring-boot-starter-webmvc

Когда человек впервые делает web‑приложение на Java, у него часто включается режим «а что если…». А что если взять другой сервер? А что если сразу reactive? А что если руками подключить только нужные модули, чтобы было «минимально»? Это всё звучит героически, но для Junior’а обычно заканчивается одинаково: половина дня уходит на сборку пазла, а не на понимание, что вообще происходит.

Поэтому в курсе мы фиксируем web‑baseline намеренно и довольно жёстко: spring-boot-starter-webmvc. Это наш «официальный вход» в web‑мир курса по трём причинам.

Первая причина — предсказуемость. Servlet‑стек и Spring MVC дают очень прямолинейную модель: пришёл HTTP‑запрос, он попал в приложение, нашёлся обработчик, вернулся ответ. Здесь минимум «скрытых слоёв мышления» для новичка. Не нужно сразу перепрошивать мозг под другие парадигмы — нам достаточно устойчивой модели request/response.

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

Третья причина — дисциплина курса. Нам важно держать один стек и один путь развития проекта. Если сегодня один студент подключит WebFlux, второй — Jetty, третий — вручную прикрутит spring-webmvc без starter’а, то завтра мы будем учить не Spring Boot, а коллективную археологию чужих зависимостей. А археология — это интересно, но лучше, когда вы уже Middle.

Чтобы не было ощущения «мне просто запретили другое», сформулирую мягче: мы не говорим «MVC лучше». Мы говорим «MVC — самый прямой путь к первому рабочему web‑сервису в рамках Boot‑курса». А когда появится прочная база, другие стеки перестанут выглядеть как магия и станут просто ещё одной инженерной опцией.

3. Starter и classpath

Spring Boot довольно циничен (в хорошем смысле): он не пытается угадать ваши мысли, он смотрит на classpath. Есть нужные классы и зависимости — значит, можно включать соответствующую инфраструктуру. Нет — значит, не включаем. Это не «чёрная магия», это прагматичная стратегия: меньше ручной настройки, больше повторяемости.

Когда вы добавляете spring-boot-starter-webmvc, происходит почти «механическое» превращение приложения в web‑приложение. И вот тут важно зафиксировать центральную мысль сегодняшней лекции: точка входа не меняется. Не появляется «особый web‑main». Мы не переходим на другой метод запуска. Мы не начинаем вызывать какие-то сервлеты руками.

main() остаётся прежним, примерно таким:

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

@SpringBootApplication // Включает component scan и auto-configuration Spring Boot
public class CatalogServiceApplication {

    public static void main(String[] args) {
        // Точка входа не меняется: web-режим включится за счёт зависимостей на classpath
        SpringApplication.run(CatalogServiceApplication.class, args);
    }
}

Меняется не код старта, а то, что старт видит вокруг себя. Вокруг появятся web‑библиотеки, а значит Boot сможет собрать web‑runtime. Если вы помните прошлые лекции про auto‑configuration, то это просто продолжение той же идеи: conditions срабатывают, потому что на classpath есть нужные элементы.

Можно представить это как переключатель режима в игре. Вы запускаете одну и ту же игру, но выбираете режим «кампания» или «мультиплеер». Кнопка «Запуск» одинаковая, а мир вокруг — другой. Starter — это как раз такой «выбор режима», только через Gradle.

Чтобы картинка была совсем чёткой, вот небольшая схема:

flowchart TD
    %% Starter меняет набор зависимостей, и это запускает другую auto-configuration цепочку
    A["build.gradle.kts: добавили webmvc starter"] --> B["Classpath: появились web-зависимости"]
    B --> C["Boot startup: срабатывают условия auto-configuration"]
    C --> D["Приложение стартует как web servlet"]
    D --> E["Процесс слушает порт и ждёт HTTP-запросы"]

Сейчас нам не нужно углубляться в детали того, какие именно компоненты поднимаются (это отдельная тема дня). Важно запомнить причинно‑следственную связь: starter → classpath → auto‑configuration → другой runtime. Это и есть Boot‑мышление.

4. Подключаем starter в build.gradle.kts

На практике подключение web‑режима — это, конечно, не философский трактат, а очень конкретная правка build.gradle.kts. И здесь хочется заранее спасти вас от типичного соблазна начинающих: «Ну раз это web, добавлю-ка я spring-web, spring-webmvc, Tomcat и ещё пять библиотек, которые нашёл в интернете». Это путь в мир, где каждый следующий шаг будет ломать предыдущий, потому что вы собираете платформу вручную.

В catalog-service мы делаем один аккуратный и понятный шаг: подключаем spring-boot-starter-webmvc как обычную зависимость implementation. Пример минимального фрагмента может выглядеть так:

dependencies {
    // Наблюдаемость и базовые метрики (удобно для проверки, что приложение живо)
    implementation("org.springframework.boot:spring-boot-starter-actuator")

    // Spring MVC + встроенный servlet-контейнер (Tomcat по умолчанию)
    implementation("org.springframework.boot:spring-boot-starter-webmvc")

    // Тестовая инфраструктура Spring Boot (JUnit, AssertJ и т.д.)
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Обратите внимание на два нюанса, которые кажутся мелочью, но реально спасают нервы.

Первый нюанс: мы не указываем версии у этих зависимостей. Версии приходят из Boot‑платформы (BOM), а значит совместимость контролируется «централизованно». Если вы добавите :spring-boot-starter-webmvc:4.0.3 руками — иногда оно «ничего не сломает», а иногда вы начнёте случайно жить в мире, где один модуль на одной версии, другой — на другой. На этом этапе курса это почти гарантированный способ создать себе загадку.

Второй нюанс: это именно starter, а не голый spring-webmvc. Starter даёт вам curated набор зависимостей и «встроенную дружелюбность» Boot. Мы не делаем вид, что умеем лучше, чем команда Boot, подобрать минимальный безопасный набор.

После изменения build.gradle.kts обычно нужно, чтобы Gradle и IDE подтянули зависимости. Иногда вы буквально видите, как у проекта появляется «вторая жизнь»: скачиваются артефакты, IDE индексирует классы, и вдруг автодополнение начинает подсказывать web‑сущности. Это нормальный сигнал: classpath поменялся.

Если хочется убедиться, что starter действительно что-то привнёс (а не просто «аннотации»), можно заглянуть в runtimeClasspath. Пример команды (без фанатизма, чисто посмотреть):

./gradlew dependencies --configuration runtimeClasspath | grep -E "webmvc|tomcat|jackson"
# runtimeClasspath показывает, что реально окажется в приложении при запуске
# grep здесь просто подсвечивает «подозреваемых»: MVC, встроенный сервер и JSON

Мы не делаем из этого отдельное расследование. Нам важен сам факт: starter «тащит» за собой инфраструктуру. Именно для этого он и существует.

5. Проверяем web-режим

После подключения starter’а у новичков часто появляется странная тревога: «Я ничего не написал, а оно уже web? Так не бывает». Бывает. Boot как раз про то, чтобы базовая инфраструктура поднималась по договорённостям, а вы добавляли только прикладной код. Но, конечно, хочется увидеть доказательства — и это абсолютно здоровое желание.

Самый простой способ проверить — запустить приложение и внимательно посмотреть на startup logs. Команда запуска может быть той же, что и раньше:

./gradlew bootRun
# Запускаем приложение через Gradle: так проще повторять одинаковый сценарий на разных машинах
# Признаки web-режима дальше будут видны в логах (поднялся сервер, слушается порт)

В логах вы обычно увидите строки, из которых понятно, что поднят встроенный сервер и он слушает порт. Примерно в таком духе (формулировка может отличаться, но смысл узнаваем):

Tomcat started on port 8080 (http) with context path '/'
Started CatalogServiceApplication in 1.234 seconds

Дальше можно сделать «самую честную проверку в мире backend’а»: постучаться в localhost. Пока у нас ещё нет controller’ов и маршрутов, корень / будет отвечать ошибкой 404 — и это нормально. Важно: 404 означает «маршрут не найден», а не «приложение не работает».

Например:

curl -i http://localhost:8080/
# -i: печатаем статус и заголовки, чтобы увидеть, что сервер реально отвечает
HTTP/1.1 404
Content-Type: application/json
...

Если вы получили HTTP‑ответ, значит приложение реально слушает порт, встроенный сервер поднят, и мы действительно в web‑режиме. Даже если ответ пока «не красивый», мы уже перешли ключевую границу: сервис доступен извне.

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

6. Типичные ошибки при web-входе

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

Ошибка №1: подключать web-стек вручную россыпью зависимостей.
Обычно это начинается с мысли «starter — это слишком много, возьму только spring-webmvc». А через двадцать минут добавляется ещё что-то «для JSON», потом что-то «для сервера», потом «почему-то не стартует» — и в итоге получаем тот же starter, но собранный случайно и с риском конфликтов. На уровне курса правильнее держаться одного аккуратного входа: spring-boot-starter-webmvc.

Ошибка №2: думать, что нужен “особый web main” или другой способ запуска.
Иногда хочется переписать main() или добавить какие-то дополнительные вызовы, чтобы «включить web». Но web‑режим в Boot включается не кодом запуска, а сочетанием classpath + auto‑configuration. Ваш SpringApplication.run(...) остаётся тем же; меняется то, какие подсистемы Boot может собрать вокруг него.

Ошибка №3: случайно подключить не тот web starter (или подключить сразу два).
Самый частый вариант — перепутать стек и добавить webflux вместо MVC, или в порыве энтузиазма добавить оба. В лучшем случае это создаст путаницу, в худшем — начнутся странные конфликты и “почему оно работает не так”. В рамках курса мы фиксируем ровно один вариант: spring-boot-starter-webmvc.

Ошибка №4: увидеть 404 и решить, что “ничего не получилось”.
После добавления starter’а приложение поднимет сервер, но маршрутов у него пока нет. Поэтому 404 на / — ожидаемое поведение. Реальный признак успеха сейчас не «красивый ответ», а то, что порт слушается и вы получаете любой HTTP‑ответ от процесса.

Ошибка №5: забыть про “старый запущенный процесс” и получить конфликт порта.
Если приложение уже было запущено, а вы стартуете ещё одну копию (или не остановили предыдущую), то порт 8080 может быть занят. Тогда кажется, что «starter всё сломал», хотя на самом деле вы просто устроили локальную конкуренцию за один и тот же порт. Это из тех багов, которые лечатся не чтением документации, а привычкой проверять, что именно у вас сейчас запущено.

1
Задача
Spring Boot, 10 уровень, 0 лекция
Недоступна
Подключение `spring-boot-starter-webmvc` и первый web-режим
Подключение `spring-boot-starter-webmvc` и первый web-режим
1
Задача
Spring Boot, 10 уровень, 0 лекция
Недоступна
Отчет о runtime classpath после подключения web starter
Отчет о runtime classpath после подключения web starter
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ