#java #spring #spring-boot #reactive-programming #spring-webflux
Вопрос:
Я работаю над реактивными потоками, используя Spring webflux. Я хочу извлечь переменную( name
) из середины реактивного конвейера и использовать ее в другом месте следующим образом.
public class Example {
public Mono<String> test() {
String name;
return Mono.just("some random string")
.map(s -> {
name = s.toUpperCase();
return name;
}).map(...)
.map(...)
.flatMap(...)
.map(...)
.map(result -> result name)
.doOnSuccess(res -> asyncPublish(name));
public void asyncPublish(String name) {
// logic to write to a Messaging queue asynchronously
}
}
}
Приведенный выше код не работает. Это надуманный пример, но он показывает, чего я хочу достичь.
Примечание: Я не хочу использовать несколько zip
операторов только для того, чтобы полностью перенести name
карту на последнюю карту, где я хочу ее использовать. Есть ли способ сохранить его в переменной, как показано выше, а затем использовать его где-нибудь еще, где бы он мне ни понадобился.
Ответ №1:
Вы можете использовать, например, Tuple2 для передачи значения name
вместе с измененными данными по цепочке.
return Mono.just("some random string")
.map(s -> s.toUpperCase())
.map(s -> Tuples.of(s, x(s))) // given that x(s) is the transformation of this map statement
.map(...) // keeping Tuple with the value of `name` in the first slot...
.flatMap(...) // keeping Tuple with the value of `name` in the first slot...
.map(resultTuple -> Tuples.of(resultTuple.getT1(), resultTuple.getT2() resultTuple.getT1()) // keeping Tuple with the value of `name` in the first slot...
.doOnSuccess(resultTuple -> asyncPublish(resultTuple.getT1()))
.map(resultTuple -> resultTuple.getT2()); // in case that the returned Mono should contain the modified value...
Tuples
находится в упаковке reactor.util.function
и является частью активной зоны реактора.
Другим способом (без передачи значения по цепочке с использованием кортежей) может быть использование AtomicReference
(но я все еще думаю, что способ кортежа чище). Способ атомарной привязки может выглядеть следующим образом:
public Mono<String> test() {
final AtomicReference<String> nameRef = new AtomicReference<>();
return Mono.just("some random string")
.map(s -> {
final String name = s.toUpperCase();
nameRef.set(name);
return name;
}).map(...)
.map(...)
.flatMap(...)
.map(...)
.map(result -> result nameRef.get())
.doOnSuccess(res -> asyncPublish(nameRef.get()));
public void asyncPublish(String name) {
// logic to write to a Messaging queue asynchronously
}
}
Комментарии:
1. Но что, если я захочу использовать переменную в doOnSuccess()? Позвольте мне также обновить этот пост
2. @justAnotherDev Я соответствующим образом обновил свой ответ. Вы все равно сможете использовать для этого подход кортежей, как показано в примере кода, содержащемся в моем ответе.
3. Круто. Кроме того, есть ли какой-либо другой способ, который не использует кортежи? Я стараюсь избегать кортежей, как в конвейере, который я в конечном итоге буду использовать
tupleVariable.getT1()
везде.4. @justAnotherDev Атомарная ссылка может быть альтернативой (я добавил пример в свой ответ). Но, возможно, способ кортежа все еще является более чистым решением.
5. Спасибо. На самом деле мне нравятся оба ваших решения. Причина, по которой я думаю, что кортежи могут быть не очень хорошей идеей, заключается в том, что если у нас много операторов в реактивном конвейере, нам придется использовать
tuple.getT1()
илиtuple.getT2()
в нескольких местах. Допустим , у нас есть 10 операторов, и только 1 из них нуженtuple.getT2()
, в этом случае мы в конечном итоге напишем много ненужногоtuple.getT1()
, где мы могли бы напрямую использоватьvalue
вместо atuple
.