MVC

Model–View–Controller разделяет представление, обработку пользовательского ввода и данные/состояние экрана.

  • Model — данные, которые нужны представлению; не обязательно JPA-сущность;
  • View — превращает model в HTML или другое представление;
  • Controller — принимает ввод, вызывает прикладной слой и выбирает ответ.

В REST-приложении Spring MVC controller чаще возвращает DTO, а HttpMessageConverter сериализует его в JSON. Это всё ещё request-response модель Spring MVC, хотя серверная HTML-view не используется.

DispatcherServlet

DispatcherServlet — front controller Spring MVC. Типичный путь запроса:

flowchart LR
    A["HTTP request"] --> B["Filter chain"]
    B --> C["DispatcherServlet"]
    C --> D["HandlerMapping"]
    D --> E["HandlerAdapter"]
    E --> F["Controller"]
    F --> G["Application service"]
    F --> H["ViewResolver or MessageConverter"]
    H --> I["HTTP response"]
  1. Servlet-фильтры обрабатывают запрос до Spring MVC.
  2. HandlerMapping находит handler по path, method и другим условиям.
  3. HandlerAdapter вызывает подходящий controller-метод.
  4. Argument resolvers собирают параметры, DTO и служебные объекты.
  5. Controller вызывает use case.
  6. Результат преобразуется view renderer или message converter.
  7. Исключение может обработать HandlerExceptionResolver/@ControllerAdvice.

Пример REST-controller

@RestController
@RequestMapping("/orders")
class OrderController {
    private final OrderQueryService orders;
 
    OrderController(OrderQueryService orders) {
        this.orders = orders;
    }
 
    @GetMapping("/{id}")
    OrderResponse get(@PathVariable long id) {
        return orders.get(id);
    }
 
    @PostMapping
    ResponseEntity<OrderResponse> create(
            @Valid @RequestBody CreateOrderRequest request) {
        OrderResponse created = orders.create(request);
        return ResponseEntity
                .created(URI.create("/orders/" + created.id()))
                .body(created);
    }
}

Controller координирует HTTP-детали, но не содержит транзакционный алгоритм и прямую работу с EntityManager.

DTO и сущности

Возврат JPA-сущности наружу создаёт проблемы:

  • lazy-загрузка во время сериализации;
  • циклические связи;
  • случайная публикация внутренних полей;
  • API становится связан со схемой хранения.

DTO фиксирует внешний контракт и позволяет валидировать вход независимо от domain model.

Filter, Interceptor, AOP

  • servlet Filter видит сырой request/response и работает до DispatcherServlet;
  • HandlerInterceptor знает выбранный MVC handler;
  • @ControllerAdvice централизует обработку MVC-ошибок;
  • AOP перехватывает вызовы Spring-бинов и не является HTTP-механизмом.

Выбор зависит от уровня данных, который нужен задаче.

Потоки

Singleton-controller обслуживает запросы из разных потоков. В нём нельзя хранить изменяемое состояние конкретного запроса в полях. Локальные переменные и immutable dependencies безопаснее.

В обычном Spring MVC один request обычно занимает servlet-thread до формирования ответа. Async MVC освобождает контейнерный поток на части ожидания, но отличается от полной reactive-модели WebFlux.

На собеседовании

Кто вызывает controller? DispatcherServlet через выбранные HandlerMapping и HandlerAdapter.

@Controller и @RestController? @RestController включает семантику @ResponseBody: возвращаемое значение записывается в response через converter, а не трактуется как имя view.

Связанные заметки: Контроллеры Spring, Spring WEB, Http протокол, Фильтры Filters слушатели Listeners и перехватчики Interceptors.