Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this short tutorial, we’ll explore various listeners of the Mono object from Spring 5 WebFlux. We’ll compare the doOnNext() and doOnSuccess() methods and discover that, even though they’re similar, they behave differently for empty Monos.

2. doOnNext

Mono‘s doOnNext() allows us to attach a listener that will be triggered when the data is emitted. For the code examples in this article, we’ll use the PaymentService class. In this case, we’ll call the processPayment method only when the paymentMono emits the data, using doOnNext():

@Test
void givenAPaymentMono_whenCallingServiceOnNext_thenCallServiceWithPayment() {
    Payment paymentOf100 = new Payment(100);
    Mono<Payment> paymentMono = Mono.just(paymentOf100);

    paymentMono.doOnNext(paymentService::processPayment)
        .block();

    verify(paymentService).processPayment(paymentOf100);
}

However, an empty Mono will not emit any data, and doOnNext will not be triggered. Consequently, if we repeat the test using Mono.empty(), the processPayment method should no longer be called:

@Test
void givenAnEmptyMono_whenCallingServiceOnNext_thenDoNotCallService() {
    Mono<Payment> emptyMono = Mono.empty();

    emptyMono.doOnNext(paymentService::processPayment)
        .block();

    verify(paymentService, never()).processPayment(any());
}

3. doOnSuccess

We can use doOnSuccess to attach a listener that will be triggered when the Mono completes successfully. Let’s repeat the test, but using doOnSuccess this time:

@Test
void givenAPaymentMono_whenCallingServiceOnSuccess_thenCallServiceWithPayment() {
    Payment paymentOf100 = new Payment(100);
    Mono<Payment> paymentMono = Mono.just(paymentOf100);

    paymentMono.doOnSuccess(paymentService::processPayment)
        .block();

    verify(paymentService).processPayment(paymentOf100);
}

Though, we should note that a Mono is considered to be completed successfully even if no data is emitted. As a result, for an empty Mono, the code above will call the processPayment method with a null Payment:

Test
void givenAnEmptyMono_whenCallingServiceOnSuccess_thenCallServiceWithNull() {
    Mono<Payment> emptyMono = Mono.empty();

    emptyMono.doOnSuccess(paymentService::processPayment)
        .block();

    verify(paymentService).processPayment(null);
}

4. Conclusion

In this short article, we learned the difference between a Mono‘s doOnNext and doOnSuccess listeners. We saw that we can use doOnNext if we want to react to the data received. On the other hand, we should use doOnSuccess if we want the method call to happen when the Mono completes successfully, regardless of whether it emits data or not.

As always, code from this article can be found over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.