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.