Spring Web MVC¶
Spring Web MVC — это модуль Spring Framework для построения веб-приложений поверх Jakarta Servlet API.
spring-web— базовый модуль: HTTP-абстракции,RestClient,RestTemplate, базовые фильтры, multipart-инфраструктура. Не содержит MVC-инфраструктуру.spring-webmvc— полный MVC-стек:DispatcherServlet, контроллеры, view resolvers, message converters и т.д. Зависит отspring-web.
MVC — паттерн разделения ответственности:
В Spring MVC:
- Model — объект Model/ModelMap, атрибуты (данные/состояние), передаваемые в представление
- View — Thymeleaf/JSP/FreeMarker-шаблон для HTML
- Controller — Java-класс с аннотацией @Controller/@RestController, принимает запрос, вызывает сервис, передает данные в View
- REST body — не классический View, а результат работы HandlerMethodReturnValueHandler + HttpMessageConverter
- Контроллеры
- Model, View и REST
- Исключения, Interceptors и Filters
- Файлы, async, локализация и сессии
- Конфигурирование, практики и тестирование
Архитектруа Spring Web MVC¶
HTTP request
-> Servlet Container
-> Servlet Filter chain
-> DispatcherServlet
-> HandlerMapping
-> HandlerExecutionChain
-> HandlerInterceptor.preHandle
-> HandlerAdapter
-> ArgumentResolvers
-> Data Binding / Validation
-> Controller method
-> ReturnValueHandlers
-> ViewResolver или HttpMessageConverter
-> HandlerInterceptor.postHandle / afterCompletion
-> HTTP response
Клиент отправляет HTTP-запрос
│
▼
Servlet Container (Tomcat / Jetty / Undertow)
│ принимает TCP/HTTP, создает HttpServletRequest / HttpServletResponse
▼
Servlet Filter chain
│ CharacterEncodingFilter, Spring Security, CORS, custom filters...
▼
DispatcherServlet
│-------------------------------------------------------------------
├─► HandlerMapping
│ └─► ищет подходящий handler (контроллер + метод)
│
├─► HandlerExecutionChain
│ └─► handler + список HandlerInterceptor
│
├─► HandlerInterceptor.preHandle(...)
│
├─► HandlerAdapter
│ └─► понимает, как вызвать найденный handler
│
├─► HandlerMethodArgumentResolver
│ └─► @PathVariable, @RequestParam, @RequestBody, Model, HttpServletRequest...
│-------------------------------------------------------------------
├─► Data Binding / Type Conversion / Validation
│
├─► Controller Method
│ └─► вызывает service layer и возвращает результат
│
├─► HandlerMethodReturnValueHandler
│ └─► решает, как обработать return value
│-------------------------------------------------------------------
├─► Либо ViewResolver + View
│ └─► если нужно рендерить HTML
│
├─► Либо HttpMessageConverter
│ └─► если нужно сериализовать JSON/XML/text в response body
│
├─► HandlerInterceptor.postHandle(...)
│
├─► ExceptionResolvers (если было исключение)
│
└─► HandlerInterceptor.afterCompletion(...)
│
▼
Клиент получает HTTP response
Spring Web MVC — это Servlet-based web framework, в котором вся обработка HTTP-запроса централизована через DispatcherServlet.
Он работает по паттерну Front Controller: • все HTTP-запросы приходят в одну точку входа, • дальше запрос делегируется нужному контроллеру, • после выполнения контроллера Spring сам решает, как построить ответ: • отрендерить HTML через View, • вернуть JSON через HttpMessageConverter, • сделать redirect, • вернуть файл, • отдать ResponseEntity.
Servlet stack — это синхронная, блокирующая модель:
- каждый запрос обрабатывается одним потоком из пула,
- поток занят до тех пор, пока не сформирован ответ,
- для конкурентности используется пул потоков (ThreadPoolExecutor в Tomcat).
Servlet Container (Tomcat, Jetty, Undertow):
- принимает TCP/HTTP-соединение,
- парсирует HTTP-запрос,
- создает HttpServletRequest и HttpServletResponse,
- вызывает зарегистрированный Servlet (в нашем случае — DispatcherServlet),
- управляет пулом потоков.
Spring MVC только управляет обработкой внутри одного запроса — всё, что ниже, делает Servlet Container.
Servlet Container (Tomcat, Jetty...)
- принимает TCP/HTTP-соединение + парсит HTTP-протокол
- создает HttpServletRequest + HttpServletResponse
- выделяет поток из пула
- передает запрос в нужный Servlet
Servlet Filter chain¶
DispatcherServlet
- принимает все запросы,
- сам решает, какой handler нужен,
- вызывает инфраструктуру MVC,
- централизует обработку.
Центральный метод DispatcherServlet.doDispatch():
// Упрощенно, внутри doDispatch():
HandlerExecutionChain chain = getHandler(request); // 1. найти handler
HandlerAdapter adapter = getHandlerAdapter(chain.getHandler()); // 2. найти adapter
chain.applyPreHandle(request, response); // 3. preHandle interceptors
ModelAndView mv = adapter.handle(request, response, chain.getHandler()); // 4. вызвать контроллер
chain.applyPostHandle(request, response, mv); // 5. postHandle interceptors
processDispatchResult(request, response, chain, mv, ex); // 6. рендер / error handling
chain.triggerAfterCompletion(request, response, ex); // 7. afterCompletion
DispatcherServlet вызывает HandlerMapping, чтобы понять:
какой контроллер и какой метод должны обработать этот запрос.
Основная реализация - RequestMappingHandlerMapping
HandlerExecutionChain
HandlerInterceptor.preHandle(...)
DispatcherServlet ищет подходящий HandlerAdapter
DispatcherServlet — Front Controller Spring MVC (координатор всего).
Это обычный HttpServlet, но он:
- зарегистрирован как точка входа для всех запросов (/),
- внутри делегирует обработку другим компонентам: HandlerMapping, HandlerAdapter, ViewResolver и т.д.,
- координирует весь lifecycle запроса внутри Spring MVC.
DispatcherServlet — это HttpServlet, который является точкой входа всего Spring MVC.
Он расширяет цепочку:
Servlet -> GenericServlet -> HttpServlet -> HttpServletBean -> FrameworkServlet -> DispatcherServlet
Front Controller — паттерн, при котором один объект принимает все входящие запросы и делегирует их конкретным обработчикам.
Преимущества: - единое место для сквозных задач (логирование, безопасность, i18n), - централизованная маршрутизация, - простота добавления общего поведения.
Как он маршрутизирует запросы
DispatcherServlet использует список HandlerMapping:
1. Перебирает все зарегистрированные HandlerMapping.
2. Первый, который вернул handler — используется.
3. По умолчанию используется RequestMappingHandlerMapping (на основе @RequestMapping).
Как он взаимодействует с другими компонентами
Все компоненты — Spring-бины в WebApplicationContext:
- HandlerMapping (может быть несколько)
- HandlerAdapter (может быть несколько)
- ViewResolver (может быть несколько, с приоритетом)
- HandlerExceptionResolver
- MultipartResolver
- LocaleResolver
DispatcherServlet инициализирует их при старте. Если не найден кастомный — использует дефолтные из DispatcherServlet.properties.
Что происходит внутри DispatcherServlet
При первом запуске (инициализация):
1. Создает WebApplicationContext.
2. Находит и инициализирует все MVC-компоненты.
При каждом запросе (doDispatch):
1. Определяет multipart или нет.
2. Ищет handler chain.
3. Обрабатывает запрос.
4. Обрабатывает исключения, если они возникли.
5. Публикует событие RequestHandledEvent.
Handler Infrastructure
HandlerMapping
Интерфейс: org.springframework.web.servlet.HandlerMapping
Основные реализации:
- RequestMappingHandlerMapping — главная, работает с @RequestMapping/@GetMapping и т.д.
- BeanNameUrlHandlerMapping — маппит URL на имя бина.
- RouterFunctionMapping — для функционального стиля WebFlux/MVC.
RequestMappingHandlerMapping при старте сканирует все @Controller-бины и строит карту:
{method, URL, params, headers, consumes, produces} -> HandlerMethod.
HandlerAdapter
Интерфейс: org.springframework.web.servlet.HandlerAdapter
Задача: знать, как вызвать конкретный тип handler.
Основные реализации:
- RequestMappingHandlerAdapter — вызывает @RequestMapping-методы контроллеров.
- SimpleControllerHandlerAdapter — вызывает старый Controller-интерфейс.
- HandlerFunctionAdapter — для функциональных handler'ов.
RequestMappingHandlerAdapter содержит список HandlerMethodArgumentResolver и HandlerMethodReturnValueHandler.
Handler execution chain
HandlerExecutionChain = handler + упорядоченный список interceptor'ов.
public class HandlerExecutionChain {
private final Object handler; // метод контроллера
private List<HandlerInterceptor> interceptorList;
}
Порядок выполнения:
1. preHandle всех interceptor'ов (по порядку)
2. сам handler
3. postHandle всех interceptor'ов (в обратном порядке)
4. afterCompletion всех interceptor'ов, чей preHandle успешно прошел (в обратном порядке)
HandlerInterceptor
Интерфейс: org.springframework.web.servlet.HandlerInterceptor
public interface HandlerInterceptor {
// до контроллера: если вернуть false — цепочка прерывается
default boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
return true;
}
// после контроллера, до рендеринга view; при @ResponseBody ModelAndView обычно null
default void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView mv) {}
// после завершения запроса для успешно пройденных interceptor'ов
default void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {}
}
Типичные применения: - логирование времени выполнения, - проверка авторизации (хотя лучше Spring Security), - установка Locale, - метрики и трейсинг, - очистка ThreadLocal.
HandlerExceptionResolver
Интерфейс: org.springframework.web.servlet.HandlerExceptionResolver
Когда в цепочке выбросилось исключение — DispatcherServlet передает его в список HandlerExceptionResolver (по приоритету).
Основные реализации:
- ExceptionHandlerExceptionResolver — обрабатывает @ExceptionHandler.
- ResponseStatusExceptionResolver — обрабатывает @ResponseStatus.
- DefaultHandlerExceptionResolver — обрабатывает стандартные Spring-исключения.
В реальном коде вокруг этого есть обработка multipart, async, exceptions и cleanup. Псевдокод выше нужен только для понимания очередности.
Поиск обработчика
HandlerMapping анализирует запрос:
- URL pattern (/users/{id})
- HTTP method (GET, POST, ...)
- headers / params / content-type
Возвращает HandlerExecutionChain = handler + список interceptor'ов.
Подготовка аргументов метода¶
HandlerAdapter (реализация RequestMappingHandlerAdapter):
- анализирует сигнатуру метода контроллера,
- для каждого параметра вызывает подходящий HandlerMethodArgumentResolver,
- результаты: @PathVariable из URI, @RequestParam из query, @RequestBody из JSON-тела и т.д.
Вызов контроллера
После подготовки аргументов — reflection-вызов метода контроллера. Контроллер возвращает результат.
Обработка результата¶
HandlerMethodReturnValueHandler обрабатывает return value:
- если @ResponseBody / @RestController -> HttpMessageConverter сериализует объект в тело ответа,
- если String / ModelAndView -> ViewResolver ищет шаблон,
- если ResponseEntity -> устанавливает статус, заголовки, тело.
Формирование HTTP-ответа
- Устанавливается HTTP status code.
- Устанавливаются response headers (
Content-Type,Cache-Control, ...). - Записывается response body (HTML, JSON, файл...).
- Servlet Container отправляет ответ клиенту.