Извлеките переменную из реактивного трубопровода spring webflux

#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 вместо a tuple .