Gymterview
middle

Что такое Spring WebFlux и чем он отличается от Spring MVC?

Spring WebFlux — реактивный веб-фреймворк в Spring, работающий на неблокирующем сервере (Netty по умолчанию). Является альтернативой Spring MVC для построения асинхронных, неблокирующих веб-приложений.

Архитектурные различия

Критерий Spring MVC Spring WebFlux
Модель выполнения Один поток на запрос (thread-per-request) Event loop (цикл событий)
Сервер Tomcat, Jetty (Servlet API) Netty, Undertow (неблокирующий)
Типы возврата Object, ResponseEntity Mono, Flux
Блокирующие вызовы Допустимы Запрещены в event-loop потоках
Потоковая передача Ограничена Нативная (SSE, WebSocket)
Потребление памяти ~1 MB stack на поток Минимальное (несколько event-loop потоков)
Макс. concurrent запросов 200-500 (пул потоков) Десятки тысяч соединений

Аннотированные контроллеры (общий стиль для MVC и WebFlux)

Пример
@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public Mono<ResponseEntity<User>> getUser(@PathVariable Long id) {
        return userService.findById(id)
            .map(ResponseEntity::ok)
            .defaultIfEmpty(ResponseEntity.notFound().build());
    }

    @GetMapping
    public Flux<User> getAllUsers() {
        return userService.findAll();
    }
}
Функциональный стиль (Router Functions) — только WebFlux
@Configuration
public class RouterConfig {

    @Bean
    public RouterFunction<ServerResponse> routes(UserHandler handler) {
        return RouterFunctions.route()
            .GET("/api/users/{id}", handler::getUser)
            .GET("/api/users", handler::getAllUsers)
            .POST("/api/users", handler::createUser)
            .build();
    }
}

@Component
public class UserHandler {

    private final UserService userService;

    public Mono<ServerResponse> getUser(ServerRequest request) {
        Long id = Long.parseLong(request.pathVariable("id"));
        return userService.findById(id)
            .flatMap(user -> ServerResponse.ok().bodyValue(user))
            .switchIfEmpty(ServerResponse.notFound().build());
    }

    public Mono<ServerResponse> getAllUsers(ServerRequest request) {
        return ServerResponse.ok().body(userService.findAll(), User.class);
    }
}

Когда выбирать WebFlux

  • Высокая конкурентность (тысячи одновременных соединений)
  • Streaming-сценарии (SSE, WebSocket, реактивные потоки)
  • Полностью неблокирующий стек (R2DBC, WebClient, reactive Redis/Mongo)
  • Микросервисы с интенсивным межсервисным взаимодействием

Когда оставаться на Spring MVC

  • Блокирующие зависимости (JDBC, JPA/Hibernate)
  • Простая CRUD-логика без требований к высокой конкурентности
  • Команда не знакома с реактивным программированием
  • Уже работающее приложение на MVC

Частые ошибки

  • Использование JDBC/JPA в WebFlux — блокирующие вызовы заблокируют event-loop; нужен R2DBC или Schedulers.boundedElastic()
  • Выбор WebFlux «потому что это новее» — если нет требований к конкурентности, MVC проще
  • Смешивание блокирующего и реактивного кода без subscribeOn — незаметно блокирует event-loop
  • Отладка реактивных цепочек — нужен Hooks.onOperatorDebug() или checkpoint()

Как используется в 2026

  • С Virtual Threads (Java 21) Spring MVC покрывает большинство сценариев, ранее требовавших WebFlux
  • Spring Boot 3.2+: spring.threads.virtual.enabled=true для MVC на виртуальных потоках — проще, чем WebFlux
  • WebFlux остаётся для: streaming (SSE, WebSocket), реактивных БД, приложений с очень высокой конкурентностью
  • Тренд: WebFlux для edge-сервисов и API Gateway, MVC + Virtual Threads для бизнес-логики

На собеседовании: важно показать понимание trade-offs, а не просто сказать «WebFlux — реактивный». Назовите модель выполнения (event-loop vs thread-per-request), и когда каждый вариант оправдан. Частая ошибка — не упомянуть, что WebFlux поддерживает два стиля: аннотированные контроллеры и Router Functions.