Перейти к содержанию

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


Архитектруа 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

DispatcherServletFront 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 отправляет ответ клиенту.