Spring Web MVC¶
Spring Web MVC - модуль Spring Framework для веб-приложений поверх Jakarta Servlet API. Вся обработка запроса
идёт через одну точку входа - DispatcherServlet.
spring-web- базовый модуль: HTTP-абстракции,RestClient,RestTemplate, базовые фильтры, multipart-инфраструктура. MVC в нём нетspring-webmvc- полный MVC-стек:DispatcherServlet, контроллеры, view resolvers, message converters. Зависит отspring-web
MVC в Spring:
- Model -
ModelилиModelMap: атрибуты, которые передаются в представление - View - шаблон Thymeleaf, JSP, FreeMarker для HTML
- Controller - класс с
@Controllerили@RestController: принимает запрос, вызывает сервис, отдаёт данные во View - REST body - не классический View, а результат
HandlerMethodReturnValueHandler+HttpMessageConverter
Порядок страниц раздела - по пути запроса: архитектура (эта страница) → контроллеры → Model, View и REST → ошибки, interceptors и фильтры → файлы, async, сессии → конфигурация, практики и тесты.
Servlet stack¶
Spring MVC работает внутри servlet-контейнера и управляет обработкой только внутри одного запроса. Всё ниже делает контейнер.
Servlet Container (Tomcat, Jetty, Undertow):
- принимает TCP/HTTP-соединение и разбирает HTTP
- создаёт
HttpServletRequestиHttpServletResponse - выделяет поток из пула (
ThreadPoolExecutorв Tomcat) - вызывает зарегистрированный
Servlet, у нас этоDispatcherServlet - перед сервлетом прогоняет цепочку servlet-фильтров (см. Filters)
Модель - синхронная и блокирующая:
- каждый запрос обрабатывает один поток из пула
- поток занят, пока не сформирован ответ
- конкурентность даёт размер пула потоков
Путь запроса¶
Клиент отправляет HTTP-запрос
│
▼
Servlet Container (Tomcat / Jetty / Undertow)
│ принимает TCP/HTTP, создаёт HttpServletRequest / HttpServletResponse
▼
Servlet Filter chain
│ CharacterEncodingFilter, Spring Security, CORS, свои фильтры
▼
DispatcherServlet
├─► HandlerMapping
│ └─► ищет handler: контроллер + метод
├─► HandlerExecutionChain
│ └─► handler + список HandlerInterceptor
├─► HandlerInterceptor.preHandle(...)
├─► HandlerAdapter
│ └─► знает, как вызвать найденный handler
├─► HandlerMethodArgumentResolver
│ └─► @PathVariable, @RequestParam, @RequestBody, Model, HttpServletRequest...
├─► Data Binding / Type Conversion / Validation
├─► метод контроллера
│ └─► вызывает сервисный слой и возвращает результат
├─► HandlerMethodReturnValueHandler
│ └─► решает, что делать с результатом
├─► либо ViewResolver + View → HTML
├─► либо HttpMessageConverter → JSON / XML / text в тело ответа
├─► HandlerInterceptor.postHandle(...)
├─► HandlerExceptionResolver → если было исключение
└─► HandlerInterceptor.afterCompletion(...)
│
▼
Клиент получает HTTP-ответ
Итого: контроллер видит только готовые аргументы и возвращает объект. Всё остальное - найти метод, собрать
аргументы, превратить результат в HTML или JSON - делает инфраструктура DispatcherServlet.
DispatcherServlet¶
DispatcherServlet - Front Controller Spring MVC: обычный HttpServlet, зарегистрированный на /, который
принимает все запросы и раздаёт их остальным компонентам.
Front Controller - паттерн, при котором один объект принимает все запросы и делегирует их обработчикам. Даёт одно место для сквозных задач (логирование, безопасность, i18n), централизованную маршрутизацию и простое добавление общего поведения.
Servlet -> GenericServlet -> HttpServlet -> HttpServletBean -> FrameworkServlet -> DispatcherServlet
Компоненты, которые он координирует - это Spring-бины в WebApplicationContext:
HandlerMapping- может быть несколькоHandlerAdapter- может быть несколькоViewResolver- может быть несколько, с приоритетомHandlerExceptionResolverMultipartResolverLocaleResolver
Своего бина нет - берётся дефолтный из DispatcherServlet.properties.
При старте DispatcherServlet получает WebApplicationContext (без Boot создаёт свой, в Boot берёт общий контекст
приложения) и находит или создаёт все MVC-компоненты.
На каждый запрос - doDispatch(). Упрощённо:
HandlerExecutionChain chain = getHandler(request); // 1. найти handler
HandlerAdapter adapter = getHandlerAdapter(chain.getHandler()); // 2. найти adapter
chain.applyPreHandle(request, response); // 3. preHandle interceptor'ов
ModelAndView mv = adapter.handle(request, response, chain.getHandler()); // 4. вызвать контроллер
chain.applyPostHandle(request, response, mv); // 5. postHandle
processDispatchResult(request, response, chain, mv, ex); // 6. рендер или обработка ошибки
chain.triggerAfterCompletion(request, response, ex); // 7. afterCompletion
В реальном коде вокруг этого обработка multipart, async, исключений и очистка ресурсов. После запроса публикуется
событие RequestHandledEvent.
Handler-инфраструктура¶
HandlerMapping - поиск обработчика: какой контроллер и метод отвечают на этот запрос.
- смотрит URL-шаблон (
/users/{id}), HTTP-метод, заголовки, параметры, content-type - возвращает
HandlerExecutionChain DispatcherServletперебирает всеHandlerMappingпо порядку, побеждает первый, который вернул handler
| Реализация | Что маппит |
|---|---|
RequestMappingHandlerMapping |
Главная: методы с @RequestMapping, @GetMapping и т.д. |
BeanNameUrlHandlerMapping |
URL на имя бина |
RouterFunctionMapping |
Функциональные роуты (RouterFunction) |
RequestMappingHandlerMapping при старте сканирует все @Controller-бины и строит карту
{метод, URL, params, headers, consumes, produces} -> HandlerMethod.
HandlerAdapter - знание, как вызвать конкретный тип handler'а.
| Реализация | Что вызывает |
|---|---|
RequestMappingHandlerAdapter |
Методы контроллеров с @RequestMapping |
SimpleControllerHandlerAdapter |
Старый интерфейс Controller |
HandlerFunctionAdapter |
Функциональные handler'ы |
RequestMappingHandlerAdapter держит списки HandlerMethodArgumentResolver и HandlerMethodReturnValueHandler:
- разбирает сигнатуру метода контроллера
- для каждого параметра вызывает подходящий resolver:
@PathVariableиз URI,@RequestParamиз query,@RequestBodyиз тела (см. Параметры методов) - вызывает метод через reflection
- отдаёт результат подходящему return value handler'у
HandlerExecutionChain - handler + упорядоченный список interceptor'ов.
public class HandlerExecutionChain {
private final Object handler; // метод контроллера
private List<HandlerInterceptor> interceptorList;
}
Порядок выполнения:
preHandleвсех interceptor'ов по порядку. Если один вернулfalse, цепочка прерывается- сам handler
postHandleв обратном порядкеafterCompletionв обратном порядке, только для interceptor'ов, чейpreHandleпрошёл
Что делает каждый метод interceptor'а и чем он отличается от фильтра - в Interceptors.
HandlerMethodReturnValueHandler - обработка результата метода контроллера:
@ResponseBodyили@RestController-HttpMessageConverterсериализует объект в тело ответаStringилиModelAndView-ViewResolverищет шаблонResponseEntity- выставляет статус, заголовки и тело
См. Model, View и REST.
HandlerExceptionResolver - обработка исключения, которое вылетело в цепочке. DispatcherServlet передаёт его
резолверам по приоритету:
ExceptionHandlerExceptionResolver- методы@ExceptionHandlerResponseStatusExceptionResolver- исключения с@ResponseStatusDefaultHandlerExceptionResolver- стандартные исключения Spring
См. Обработка исключений.
Формирование ответа¶
- Выставляется HTTP-статус.
- Выставляются заголовки:
Content-Type,Cache-Controlи т.д. - Записывается тело: HTML, JSON, файл.
- Servlet-контейнер отправляет ответ клиенту.
Что может вернуть контроллер: отрендеренный HTML через View, JSON через HttpMessageConverter, redirect, файл,
ResponseEntity (см. Возвращаемые значения).