diff --git a/spring-reactive-modules/spring-reactive-client-2/pom.xml b/spring-reactive-modules/spring-reactive-client-2/pom.xml
index 1cdfead3f1..26609e716c 100644
--- a/spring-reactive-modules/spring-reactive-client-2/pom.xml
+++ b/spring-reactive-modules/spring-reactive-client-2/pom.xml
@@ -58,6 +58,19 @@
spring-boot-devtools
runtime
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+ test
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+ test
+
+
org.springframework
spring-test
@@ -141,6 +154,7 @@
1.0.1.RELEASE
1.1.6
1.7.1
+ 1.37
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeApp.java b/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeApp.java
new file mode 100644
index 0000000000..aac9533b5f
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeApp.java
@@ -0,0 +1,12 @@
+package com.baeldung.webclientretrievevsexchange;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class RetrieveAndExchangeApp {
+
+ public static void main(String[] args) {
+ SpringApplication.run(RetrieveAndExchangeApp.class, args);
+ }
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeController.java b/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeController.java
new file mode 100644
index 0000000000..7cac164363
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeController.java
@@ -0,0 +1,135 @@
+package com.baeldung.webclientretrievevsexchange;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@RestController
+public class RetrieveAndExchangeController {
+
+ private static final Logger logger = LoggerFactory.getLogger(RetrieveAndExchangeController.class);
+
+ WebClient client = WebClient.create("https://jsonplaceholder.typicode.com/users");
+
+ @GetMapping("/user/{id}")
+ Mono retrieveOneUser(@PathVariable int id) {
+ return client.get()
+ .uri("/{id}", id)
+ .retrieve()
+ .bodyToMono(User.class)
+ .onErrorResume(Mono::error);
+ }
+
+ @GetMapping("/user-status/{id}")
+ Mono retrieveOneUserAndHandleErrorBasedOnStatus(@PathVariable int id) {
+ return client.get()
+ .uri("/{id}", id)
+ .retrieve()
+ .onStatus(HttpStatusCode::is4xxClientError, response -> Mono.error(new RuntimeException("Client Error: can't fetch user")))
+ .onStatus(HttpStatusCode::is5xxServerError, response -> Mono.error(new RuntimeException("Server Error: can't fetch user")))
+ .bodyToMono(User.class);
+ }
+
+ @GetMapping("/user-id/{id}")
+ Mono> retrieveOneUserWithResponseEntity(@PathVariable int id) {
+ return client.get()
+ .uri("/{id}", id)
+ .accept(MediaType.APPLICATION_JSON)
+ .retrieve()
+ .toEntity(User.class)
+ .onErrorResume(Mono::error);
+ }
+
+ @GetMapping("/users")
+ Flux retrieveAllUsers() {
+ return client.get()
+ .retrieve()
+ .bodyToFlux(User.class)
+ .onErrorResume(Flux::error);
+ }
+
+ @GetMapping("/user/exchange-alter/{id}")
+ Mono retrieveOneUserWithExchangeAndManipulate(@PathVariable int id) {
+ return client.get()
+ .uri("/{id}", id)
+ .exchangeToMono(res -> res.bodyToMono(User.class))
+ .map(user -> {
+ user.setName(user.getName()
+ .toUpperCase());
+ user.setId(user.getId() + 100);
+ return user;
+ });
+ }
+
+ @GetMapping("/user/exchange-mono/{id}")
+ Mono retrieveUsersWithExchangeAndError(@PathVariable int id) {
+ return client.get()
+ .uri("/{id}", id)
+ .exchangeToMono(res -> {
+ if (res.statusCode()
+ .is2xxSuccessful()) {
+ return res.bodyToMono(User.class);
+ } else if (res.statusCode()
+ .is4xxClientError()) {
+ return Mono.error(new RuntimeException("Client Error: can't fetch user"));
+ } else if (res.statusCode()
+ .is5xxServerError()) {
+ return Mono.error(new RuntimeException("Server Error: can't fetch user"));
+ } else {
+ return res.createError();
+ }
+ });
+ }
+
+ @GetMapping("/user/exchange-header/{id}")
+ Mono retrieveUsersWithExchangeAndHeader(@PathVariable int id) {
+ return client.get()
+ .uri("/{id}", id)
+ .exchangeToMono(res -> {
+ if (res.statusCode()
+ .is2xxSuccessful()) {
+ logger.info("Status code: " + res.headers()
+ .asHttpHeaders());
+ logger.info("Content-type" + res.headers()
+ .contentType());
+ return res.bodyToMono(User.class);
+ } else if (res.statusCode()
+ .is4xxClientError()) {
+ return Mono.error(new RuntimeException("Client Error: can't fetch user"));
+ } else if (res.statusCode()
+ .is5xxServerError()) {
+ return Mono.error(new RuntimeException("Server Error: can't fetch user"));
+ } else {
+ return res.createError();
+ }
+ });
+ }
+
+ @GetMapping("/user-exchange")
+ Flux retrieveAllUserWithExchange(@PathVariable int id) {
+ return client.get()
+ .exchangeToFlux(res -> res.bodyToFlux(User.class))
+ .onErrorResume(Flux::error);
+ }
+
+ @GetMapping("/user-exchange-flux")
+ Flux retrieveUsersWithExchange() {
+ return client.get()
+ .exchangeToFlux(res -> {
+ if (res.statusCode()
+ .is2xxSuccessful()) {
+ return res.bodyToFlux(User.class);
+ } else {
+ return Flux.error(new RuntimeException("Error while fetching users"));
+ }
+ });
+ }
+}
diff --git a/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/User.java b/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/User.java
new file mode 100644
index 0000000000..cc9363c688
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-client-2/src/main/java/com/baeldung/webclientretrievevsexchange/User.java
@@ -0,0 +1,26 @@
+package com.baeldung.webclientretrievevsexchange;
+
+public class User {
+
+ private int id;
+ private String name;
+
+ public User() {
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/spring-reactive-modules/spring-reactive-client-2/src/test/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeBenchmark.java b/spring-reactive-modules/spring-reactive-client-2/src/test/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeBenchmark.java
new file mode 100644
index 0000000000..160d6d15a4
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-client-2/src/test/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeBenchmark.java
@@ -0,0 +1,63 @@
+package com.baeldung.webclientretrievevsexchange;
+
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.concurrent.TimeUnit;
+
+@State(Scope.Benchmark)
+@BenchmarkMode(Mode.AverageTime)
+@Warmup(iterations = 3, time = 10, timeUnit = TimeUnit.MICROSECONDS)
+@Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.MICROSECONDS)
+public class RetrieveAndExchangeBenchmark {
+ private WebClient client;
+
+ @Setup
+ public void setup() {
+ this.client = WebClient.create("https://jsonplaceholder.typicode.com/users");
+ }
+
+ @Benchmark
+ public Mono retrieveOneUserUsingRetrieveMethod() {
+ return client.get()
+ .uri("/1")
+ .retrieve()
+ .bodyToMono(User.class)
+ .onErrorResume(Mono::error);
+
+ }
+
+ @Benchmark
+ public Flux retrieveManyUserUsingRetrieveMethod() {
+ return client.get()
+ .retrieve()
+ .bodyToFlux(User.class)
+ .onErrorResume(Flux::error);
+ }
+
+ @Benchmark
+ public Mono retrieveOneUserUsingExchangeToMono() {
+ return client.get()
+ .uri("/1")
+ .exchangeToMono(res -> res.bodyToMono(User.class))
+ .onErrorResume(Mono::error);
+ }
+
+ @Benchmark
+ public Flux retrieveManyUserUsingExchangeToFlux() {
+ return client.get()
+ .exchangeToFlux(res -> res.bodyToFlux(User.class))
+ .onErrorResume(Flux::error);
+
+ }
+
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-client-2/src/test/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeIntegrationTest.java b/spring-reactive-modules/spring-reactive-client-2/src/test/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeIntegrationTest.java
new file mode 100644
index 0000000000..8343f63a37
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-client-2/src/test/java/com/baeldung/webclientretrievevsexchange/RetrieveAndExchangeIntegrationTest.java
@@ -0,0 +1,68 @@
+package com.baeldung.webclientretrievevsexchange;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+@WebFluxTest
+class RetrieveAndExchangeIntegrationTest {
+
+ @Autowired
+ private WebTestClient webTestClient;
+
+ @Test
+ void givenFirstUser_whenRetrieveMethodIsUsed_thenReturnOk() throws Exception {
+ this.webTestClient.get()
+ .uri("/user/1")
+ .exchange()
+ .expectStatus()
+ .isOk();
+ }
+
+ @Test
+ void givenFirstUser_whenRetreiveMethodIsUsedWithOnStatusHandler_thenReturnNotFound() throws Exception {
+ this.webTestClient.get()
+ .uri("/user-status/100")
+ .exchange()
+ .expectStatus()
+ .is5xxServerError();
+ }
+
+ @Test
+ void givenFirstUser_whenExchangeMonoMethodIsUsed_thenReturnOk() throws Exception {
+ this.webTestClient.get()
+ .uri("/user/exchange-mono/1")
+ .exchange()
+ .expectStatus()
+ .isOk();
+ }
+
+ @Test
+ void givenAllUsers_whenRetrieveMethodIsUsed_thenReturnOk() throws Exception {
+ this.webTestClient.get()
+ .uri("/users")
+ .exchange()
+ .expectStatus()
+ .isOk();
+ }
+
+ @Test
+ void givenSingleUser_whenResponseBodyIsAltered_thenReturnOk() throws Exception {
+ this.webTestClient.get()
+ .uri("/user/exchange-alter/1")
+ .exchange()
+ .expectBody()
+ .json("{\"id\":101,\"name\":\"LEANNE GRAHAM\"}");
+ }
+
+ @Test
+ void givenAllUsers_whenExchangeFluxMethodIsUsed_thenReturnOk() throws Exception {
+ this.webTestClient.get()
+ .uri("/user-exchange-flux")
+ .exchange()
+ .expectStatus()
+ .isOk();
+ }
+
+}
\ No newline at end of file