Gymterview
junior

Чем отличаются Mono и Flux в Project Reactor?

Mono — реактивный поток, эмитирующий 0 или 1 элемент. Flux — реактивный поток, эмитирующий от 0 до N элементов. Оба реализуют интерфейс Publisher и являются ленивыми — ничего не происходит до подписки.

Пример
// Flux: 0..N элементов
Flux<String> flux = Flux.just("Spring", "Reactor", "WebFlux");
Flux<Integer> range = Flux.range(1, 100);
Flux<Long> interval = Flux.interval(Duration.ofSeconds(1)); // бесконечный поток

// Mono: 0..1 элемент
Mono<String> mono = Mono.just("Hello");
Mono<User> user = Mono.fromCallable(() -> userRepository.findById(1L));
Mono<Void> empty = Mono.empty(); // сигнал без данных (аналог void)

Ключевые различия

Критерий Mono Flux
Количество элементов 0 или 1 0…N
Аналог в блокирующем мире Optional, CompletableFuture List, Stream
Типичное применение Запрос к БД по ID, HTTP-запрос Список сущностей, поток событий
Backpressure Не актуально (максимум 1 элемент) Критически важно

Конвертация между Mono и Flux

Пример
// Flux -> Mono
Mono<String> first = Flux.just("A", "B", "C").next();           // первый элемент
Mono<List<String>> all = Flux.just("A", "B", "C").collectList(); // все в список
Mono<Long> count = Flux.just("A", "B", "C").count();             // количество

// Mono -> Flux
Flux<String> flux = Mono.just("A").flux();                       // Mono как Flux из 1 элемента
Flux<String> repeated = Mono.just("A").repeat(3);                // повторить Mono
Типичные паттерны использования в контроллере
@RestController
public class UserController {

    @GetMapping("/users/{id}")
    public Mono<User> getUser(@PathVariable Long id) {
        return userRepository.findById(id); // Mono — один пользователь
    }

    @GetMapping("/users")
    public Flux<User> getAllUsers() {
        return userRepository.findAll(); // Flux — список пользователей
    }

    @PostMapping("/users")
    public Mono<Void> createUser(@RequestBody Mono<User> user) {
        return user.flatMap(userRepository::save).then(); // Mono<Void> — результат без тела
    }
}

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

  • Использовать Flux там, где достаточно Mono — если результат всегда один объект (findById), используйте Mono
  • Вызывать .block() в реактивном контексте — может привести к deadlock
  • Игнорировать возвращённый Mono/Flux — без подписки операция не выполнится
  • Путать map и flatMap — map для синхронных преобразований, flatMap когда трансформация возвращает Mono/Flux

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

  • Mono и Flux — основа реактивного стека Spring (WebFlux, Spring Data Reactive, Spring Security Reactive)
  • С Virtual Threads часть сценариев заменяется обычным блокирующим кодом
  • Mono/Flux незаменимы для streaming: SSE, WebSocket, R2DBC
  • Тренд: использование Mono/Flux только там, где действительно нужна реактивность

На собеседовании: минимум — назвать отличие (0…1 vs 0…N) и привести примеры использования каждого. Частая ошибка — не упомянуть ленивость (lazy): Mono/Flux без subscribe ничего не делают, а также забыть про Mono как реактивный аналог void.