Mono в этом примере является одним из двух основных типов Reactor. Второй это - Flux. Оба являются реализациями Reactive Streams Publisher. Поток представляет собой конвейер из нуля, одного или многих (потенциально бесконечных) элементов данных. Mono - это специализированный реактивный тип, оптимизированный для случаев, когда известно, что в наборе данных содержится не более одного элемента данных.
Reactor vs. RxJava (ReactiveX)
Если вы уже знакомы с RxJava или ReactiveX, возможно, вы думаете, что Mono и Flux звучат во многом как Observable и Single. На самом деле, они примерно эквивалентны семантически. Они даже предлагают много одинаковых операций.
Хотя в этой книге мы сосредоточимся на Reactor, вы, возможно, будете рады узнать, что можно скрывать типы Reactor и RxJava. Более того, как вы увидите в следующих главах, Spring также может работать с типами RxJava.
На самом деле в предыдущем примере три Mono. Операция just() создает первую. Когда Mono выдает значение, это значение присваивается операции map(), которая описывает что слово должно быть написано заглавными буквами и использовано для создания другого Mono. Когда второй Mono публикует свои данные, он передается второй операции map() для выполнения конкатенации строк, результаты которой используются для создания третьего Mono. Наконец, вызов метода subscribe() подписывается на Mono, получает данные и печатает их.
10.2.1 Схема реактивных потоков
Реактивные потоки часто иллюстрируются мраморными (marble) диаграммами. Мраморные (marble) диаграммы в своей простейшей форме изображают временную шкалу данных, потоков проходящих через Flux или Mono вверху, операцию в середине и временную шкалу результирующего Flux или Mono внизу. На рис. 10.1 показан шаблон диаграммы состояния потока. Как вы можете видеть, когда данные проходят через исходный Flux, он обрабатывается через некоторую операцию, в результате чего возникает новый Flux, через который проходят обработанные данные.
На рисунке 10.2 показана аналогичная мраморная (marble) диаграмма, но для Mono. Как видите, ключевое отличие состоит в том, что у Mono будет либо ноль, либо один элемент данных, либо ошибка.
В разделе 10.3 мы рассмотрим многие операции, поддерживаемые Flux и Mono, и будем использовать мраморные (marble) диаграммы для визуализации их работы.
Рисунок 10.1 Мраморная (marble) диаграмма, иллюстрирующая основной поток Flux
Рисунок 10.2 Мраморная (marble) диаграмма, иллюстрирующая основной поток Mono
10.2.2 Добавление Reactor зависимостей
Чтобы начать работу с Reactor, добавьте следующую зависимость в сборку проекта:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
Reactor также предоставляет отличную поддержку тестирования. Мы собираемся написать множество тестов для своего кода Reactor, поэтому вам обязательно нужно добавить эту зависимость в вашу сборку:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
Я предполагаю, что вы добавляете эти зависимости в проект Spring Boot, который обрабатывает управление зависимостями (dependency management) для вас, поэтому нет необходимости указывать элемент <version> для зависимостей. Но если вы хотите использовать Reactor в проекте, отличном от Spring Boot, вам потребуется настроить спецификацию Reactor BOM (bill of materials) в сборке. Следующая запись управления зависимостями добавляет Reactor Bismuth в сборку:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>Bismuth-RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Теперь, когда Reactor находится в разработке вашего проекта, вы можете начать создавать реактивные конвейеры с Mono и Flux. В оставшейся части этой главы мы рассмотрим несколько операций, предлагаемых Mono и Flux.
10.3 Применение общих реактивных операций
Flux и Mono являются наиболее важными строительными блоками, предоставляемыми Reactor, и операции, предлагаемые этими двумя реактивными типами, представляют собой раствор, который связывает их вместе для создания конвейеров, по которым могут передаваться данные. Между Flux и Mono существует более 500 операций, каждую из которых можно условно классифицировать как