Сервлет и встроенный контейнер
Сервлет-контейнер управляет жизненным циклом сервлета: init() при загрузке, service() (→ doGet/doPost) на каждый запрос, destroy() при выгрузке. В Spring MVC единственный центральный сервлет — DispatcherServlet, через него проходят все запросы.
Ключевой сдвиг Spring Boot: контейнер встроен (по умолчанию Tomcat; альтернативы — Jetty, Undertow). Приложение собирается в самодостаточный исполняемый jar со встроенным сервером, а не разворачивается WAR-файлом на внешний сервер приложений — это упрощает деплой и контейнеризацию.
Модель thread-per-request
Контейнер держит пул рабочих потоков (у Tomcat по умолчанию ~200). Каждый входящий запрос берёт один поток из пула и держит его всё время обработки, включая блокирующие ожидания (БД, внешние вызовы).
Отсюда потолок: при блокирующем IO число одновременных запросов ограничено размером пула — потоки простаивают в ожидании. Это и есть мотивация WebFlux (неблокирующая модель) и виртуальных потоков (дешёвые потоки), которым посвящены отдельные уроки.
Потокобезопасность контроллеров
Один экземпляр сервлета (и singleton-контроллера Spring) обслуживает множество запросов параллельно разными потоками. Поэтому состояние конкретного запроса НЕЛЬЗЯ хранить в полях контроллера/сервлета — это гонка данных.
Правило: в полях держат только потокобезопасные зависимости (другие бины), а данные запроса живут в параметрах метода, локальных переменных или request-scope бинах. Мутабельное поле-«состояние» в singleton-контроллере — классический баг.
Filter против Interceptor
Servlet Filter работает на уровне контейнера ДО DispatcherServlet и оборачивает всю обработку: CORS, security, сжатие, логирование, correlation id. Фильтры образуют цепочку (FilterChain), а порядок задают через @Order или FilterRegistrationBean.
HandlerInterceptor работает ВНУТРИ MVC, вокруг выбранного обработчика: preHandle/postHandle/afterCompletion, с доступом к тому, какой handler выбран. Выбор: нужна низкоуровневая обработка любого запроса и доступ к сырым servlet API — Filter; нужна логика, привязанная к MVC-обработчику — Interceptor.
forward и redirect
forward — серверная передача обработки другому ресурсу в рамках ТОГО ЖЕ запроса: новый запрос браузера не делается, URL не меняется. redirect — ответ 3xx, после которого клиент делает НОВЫЙ запрос на другой URL (URL в адресной строке меняется).
Отсюда паттерн PRG (Post/Redirect/Get): после успешного POST отвечают редиректом на GET-страницу, чтобы перезагрузка (F5) не повторяла отправку формы/платежа.
Как отвечать на собеседовании
Объясните встроенный контейнер (jar вместо WAR) и модель thread-per-request (поток из пула на запрос, блокировка на IO → мотивация WebFlux/виртуальных потоков). Подчеркните потокобезопасность singleton-контроллеров (нет состояния запроса в полях). Различите Filter (до DispatcherServlet, контейнерный, цепочка) и Interceptor (внутри MVC, вокруг handler), и forward vs redirect с паттерном PRG.