Как Spring MVC обрабатывает входящий HTTP-запрос?

«Как Spring MVC обрабатывает входящий HTTP-запрос?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Обработка запроса в Spring MVC представляет собой цепочку (pipeline) компонентов, известную как Request Processing Pipeline. Вот ключевые этапы:

1. Запрос поступает в DispatcherServlet: DispatcherServlet — это Front Controller, единая точка входа для всех запросов. Он наследуется от HttpServlet и координирует весь процесс.

2. Поиск цепочки HandlerMapping: DispatcherServlet опрашивает список HandlerMapping-бинов, чтобы определить, какой контроллер (@Controller) (или HandlerMethod) должен обработать запрос, основываясь на URL (@RequestMapping).

3. Выполнение цепочки HandlerInterceptor: Если для найденного обработчика сконфигурированы HandlerInterceptor-ы, вызываются их методы preHandle(). Они могут выполнить проверки (логирование, аутентификацию) и прервать обработку.

4. Адаптация и вызов обработчика (HandlerAdapter): DispatcherServlet находит подходящий HandlerAdapter (например, RequestMappingHandlerAdapter для @Controller), который вызывает целевой метод контроллера, разрешая аргументы и выполняя конвертацию данных.

5. Выполнение метода контроллера: Вызывается метод, аннотированный @RequestMapping, @GetMapping и т.д. Spring автоматически связывает (binds) параметры запроса, тело, заголовки, переменные пути с аргументами метода.

@RestController
public class UserController {
    @GetMapping("/users/{id}")
    public UserDto getUser(@PathVariable Long id, 
                           @RequestHeader("User-Agent") String userAgent) {
        // Бизнес-логика
        return userService.findById(id);
    }

    @PostMapping("/users")
    public ResponseEntity<UserDto> createUser(@Valid @RequestBody UserCreateRequest request) {
        UserDto created = userService.create(request);
        return ResponseEntity.created(URI.create("/users/" + created.id())).body(created);
    }
}

6. Обработка возвращаемого значения (HandlerMethodReturnValueHandler): Результат работы метода (String, ResponseEntity, DTO) обрабатывается для формирования ModelAndView. Для @RestController@ResponseBody) объект сериализуется в JSON/XML с помощью HttpMessageConverter (например, MappingJackson2HttpMessageConverter).

7. Обработка исключений (HandlerExceptionResolver): Если в процессе возникло исключение, DispatcherServlet делегирует его обработку цепочке HandlerExceptionResolver. @ControllerAdvice и @ExceptionHandler — стандартный способ глобальной обработки ошибок.

8. Рендеринг ответа (ViewResolver): Для традиционного MVC (с HTML) ViewResolver выбирает шаблон (JSP, Thymeleaf) для рендеринга. Для REST API этот шаг часто пропускается, так как ответ уже сериализован.

9. Пост-обработка в HandlerInterceptor: Вызываются методы postHandle() и afterCompletion() интерцепторов (например, для логирования времени выполнения).

10. Отправка ответа клиенту: DispatcherServlet отправляет окончательный HTTP-ответ через HttpServletResponse.

Визуализация потока:

HTTP Request
    ↓
DispatcherServlet (Front Controller)
    ↓
HandlerMapping (Находит контроллер по URL)
    ↓
HandlerInterceptor.preHandle() (Логирование, безопасность)
    ↓
HandlerAdapter (Вызывает метод контроллера)
    ↓
@Controller Method Execution (Ваша бизнес-логика)
    ↓
HandlerInterceptor.postHandle()
    ↓
Return Value Handling / Message Conversion (Объект → JSON)
    ↓
HandlerExceptionResolver (Если было исключение)
    ↓
ViewResolver (Для MVC с UI)
    ↓
HandlerInterceptor.afterCompletion()
    ↓
HTTP Response (JSON/HTML) → Клиент