From 28c0b9569e1f0aef7496fc1f6e529d4e0ba97999 Mon Sep 17 00:00:00 2001 From: Vlad Fernoaga Date: Tue, 29 Nov 2022 04:23:11 +0200 Subject: [PATCH] Feing client vs web client (#12968) * implement code example for feignClient vs WebClient * refactor code example for feignClient vs WebClient with /products endpoint * fix PR comment --- .../spring-5-reactive-3/pom.xml | 25 ++++++++ .../java/com/baeldung/webclient/Product.java | 13 ++++ .../webclient/ProductsFeignClient.java | 15 +++++ .../ProductsSlowServiceController.java | 21 +++++++ .../webclient/WebClientApplication.java | 15 +++++ .../com/baeldung/webclient/WebController.java | 59 +++++++++++++++++++ .../DataBufferToInputStreamUnitTest.java | 2 +- .../WebControllerIntegrationTest.java | 49 +++++++++++++++ 8 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/Product.java create mode 100644 spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/ProductsFeignClient.java create mode 100644 spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/ProductsSlowServiceController.java create mode 100644 spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/WebClientApplication.java create mode 100644 spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/WebController.java rename spring-reactive-modules/spring-5-reactive-3/src/test/java/{ => com/baeldung}/databuffer/DataBufferToInputStreamUnitTest.java (98%) create mode 100644 spring-reactive-modules/spring-5-reactive-3/src/test/java/com/baeldung/webclient/WebControllerIntegrationTest.java diff --git a/spring-reactive-modules/spring-5-reactive-3/pom.xml b/spring-reactive-modules/spring-5-reactive-3/pom.xml index 5d5bfbdc5b..d33b63e921 100644 --- a/spring-reactive-modules/spring-5-reactive-3/pom.xml +++ b/spring-reactive-modules/spring-5-reactive-3/pom.xml @@ -20,6 +20,14 @@ org.springframework.boot spring-boot-starter-webflux + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-openfeign + org.projectreactor reactor-spring @@ -35,10 +43,27 @@ reactor-test test + + org.projectlombok + lombok + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + 1.0.1.RELEASE + 2021.0.4 \ No newline at end of file diff --git a/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/Product.java b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/Product.java new file mode 100644 index 0000000000..f915792a31 --- /dev/null +++ b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/Product.java @@ -0,0 +1,13 @@ +package com.baeldung.webclient; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Product { + private String title; + private String description; +} diff --git a/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/ProductsFeignClient.java b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/ProductsFeignClient.java new file mode 100644 index 0000000000..d3f7f8118f --- /dev/null +++ b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/ProductsFeignClient.java @@ -0,0 +1,15 @@ +package com.baeldung.webclient; + +import java.net.URI; +import java.util.List; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@FeignClient(value = "productsBlocking", url = "http://localhost:8080") +public interface ProductsFeignClient { + + @RequestMapping(method = RequestMethod.GET, value = "/slow-service-products", produces = "application/json") + List getProductsBlocking(URI baseUrl); +} diff --git a/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/ProductsSlowServiceController.java b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/ProductsSlowServiceController.java new file mode 100644 index 0000000000..8f70c3b228 --- /dev/null +++ b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/ProductsSlowServiceController.java @@ -0,0 +1,21 @@ +package com.baeldung.webclient; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ProductsSlowServiceController { + + @GetMapping("/slow-service-products") + private List getAllProducts() throws InterruptedException { + Thread.sleep(2000L); // delay + return Arrays.asList( + new Product("Fancy Smartphone", "A stylish phone you need"), + new Product("Cool Watch", "The only device you need"), + new Product("Smart TV", "Cristal clean images") + ); + } +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/WebClientApplication.java b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/WebClientApplication.java new file mode 100644 index 0000000000..9e55211b24 --- /dev/null +++ b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/WebClientApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.webclient; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication +@EnableFeignClients +public class WebClientApplication { + + public static void main(String[] args) { + SpringApplication.run(WebClientApplication.class, args); + } +} + diff --git a/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/WebController.java b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/WebController.java new file mode 100644 index 0000000000..7ab6754d44 --- /dev/null +++ b/spring-reactive-modules/spring-5-reactive-3/src/main/java/com/baeldung/webclient/WebController.java @@ -0,0 +1,59 @@ +package com.baeldung.webclient; + +import java.net.URI; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.function.client.WebClient; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Flux; + +@Slf4j +@RestController +public class WebController { + + private static final int DEFAULT_PORT = 8080; + + public static final String SLOW_SERVICE_PRODUCTS_ENDPOINT_NAME = "/slow-service-products"; + + @Setter + private int serverPort = DEFAULT_PORT; + + @Autowired + private ProductsFeignClient productsFeignClient; + + @GetMapping("/products-blocking") + public List getProductsBlocking() { + log.info("Starting BLOCKING Controller!"); + final URI uri = URI.create(getSlowServiceBaseUri()); + + List result = productsFeignClient.getProductsBlocking(uri); + result.forEach(product -> log.info(product.toString())); + log.info("Exiting BLOCKING Controller!"); + return result; + } + + @GetMapping(value = "/products-non-blocking", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux getProductsNonBlocking() { + log.info("Starting NON-BLOCKING Controller!"); + Flux productFlux = WebClient.create() + .get() + .uri(getSlowServiceBaseUri() + SLOW_SERVICE_PRODUCTS_ENDPOINT_NAME) + .retrieve() + .bodyToFlux(Product.class); + + productFlux.subscribe(product -> log.info(product.toString())); + log.info("Exiting NON-BLOCKING Controller!"); + return productFlux; + } + + private String getSlowServiceBaseUri() { + return "http://localhost:" + serverPort; + } + +} diff --git a/spring-reactive-modules/spring-5-reactive-3/src/test/java/databuffer/DataBufferToInputStreamUnitTest.java b/spring-reactive-modules/spring-5-reactive-3/src/test/java/com/baeldung/databuffer/DataBufferToInputStreamUnitTest.java similarity index 98% rename from spring-reactive-modules/spring-5-reactive-3/src/test/java/databuffer/DataBufferToInputStreamUnitTest.java rename to spring-reactive-modules/spring-5-reactive-3/src/test/java/com/baeldung/databuffer/DataBufferToInputStreamUnitTest.java index b885919bbb..f30b4a8a3b 100644 --- a/spring-reactive-modules/spring-5-reactive-3/src/test/java/databuffer/DataBufferToInputStreamUnitTest.java +++ b/spring-reactive-modules/spring-5-reactive-3/src/test/java/com/baeldung/databuffer/DataBufferToInputStreamUnitTest.java @@ -1,4 +1,4 @@ -package databuffer; +package com.baeldung.databuffer; import com.baeldung.databuffer.DataBufferToInputStream; diff --git a/spring-reactive-modules/spring-5-reactive-3/src/test/java/com/baeldung/webclient/WebControllerIntegrationTest.java b/spring-reactive-modules/spring-5-reactive-3/src/test/java/com/baeldung/webclient/WebControllerIntegrationTest.java new file mode 100644 index 0000000000..e401fb649d --- /dev/null +++ b/spring-reactive-modules/spring-5-reactive-3/src/test/java/com/baeldung/webclient/WebControllerIntegrationTest.java @@ -0,0 +1,49 @@ +package com.baeldung.webclient; + +import static org.springframework.test.annotation.DirtiesContext.ClassMode.BEFORE_CLASS; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.web.reactive.server.WebTestClient; + +@DirtiesContext(classMode = BEFORE_CLASS) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = WebClientApplication.class) +class WebControllerIntegrationTest { + + @LocalServerPort + private int randomServerPort; + + @Autowired + private WebTestClient testClient; + + @Autowired + private WebController webController; + + @BeforeEach + void setup() { + webController.setServerPort(randomServerPort); + } + + @Test + void whenEndpointWithBlockingClientIsCalled_thenThreeProductsAreReceived() { + testClient.get() + .uri("/products-blocking") + .exchange() + .expectStatus().isOk() + .expectBodyList(Product.class).hasSize(3); + } + + @Test + void whenEndpointWithNonBlockingClientIsCalled_thenThreeProductsAreReceived() { + testClient.get() + .uri("/products-non-blocking") + .exchange() + .expectStatus().isOk() + .expectBodyList(Product.class).hasSize(3); + } +} \ No newline at end of file