From 19437f2e13555d701d79d58561590e9815aa59e1 Mon Sep 17 00:00:00 2001 From: Mladen Savic Date: Mon, 8 Nov 2021 17:06:17 +0100 Subject: [PATCH] Spring Webflux and @Cacheable Annotation --- pom.xml | 3 + spring-webflux-caching/pom.xml | 67 +++++++++++++ .../main/java/com/baeldung/caching/Item.java | 50 ++++++++++ .../com/baeldung/caching/ItemRepository.java | 8 ++ .../com/baeldung/caching/ItemService.java | 42 ++++++++ .../SpringWebfluxCachingApplication.java | 16 ++++ .../src/main/resources/application.properties | 2 + .../MonoFluxResultCachingLiveTest.java | 95 +++++++++++++++++++ 8 files changed, 283 insertions(+) create mode 100644 spring-webflux-caching/pom.xml create mode 100644 spring-webflux-caching/src/main/java/com/baeldung/caching/Item.java create mode 100644 spring-webflux-caching/src/main/java/com/baeldung/caching/ItemRepository.java create mode 100644 spring-webflux-caching/src/main/java/com/baeldung/caching/ItemService.java create mode 100644 spring-webflux-caching/src/main/java/com/baeldung/caching/SpringWebfluxCachingApplication.java create mode 100644 spring-webflux-caching/src/main/resources/application.properties create mode 100644 spring-webflux-caching/src/test/java/com/baeldung/caching/MonoFluxResultCachingLiveTest.java diff --git a/pom.xml b/pom.xml index f2a53f38b7..9a34ed0afe 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,9 @@ com.baeldung parent-modules 1.0.0-SNAPSHOT + + spring-webflux-caching + parent-modules pom diff --git a/spring-webflux-caching/pom.xml b/spring-webflux-caching/pom.xml new file mode 100644 index 0000000000..ed9800bce9 --- /dev/null +++ b/spring-webflux-caching/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + com.baeldung.spring + spring-webflux-caching + 1.0.0-SNAPSHOT + spring-webflux-caching + jar + Spring WebFlux Caching Sample + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + + org.springframework.boot + spring-boot-starter-webflux + + + io.reactivex.rxjava2 + rxjava + 2.2.19 + + + io.projectreactor.addons + reactor-extra + 3.4.5 + + + com.github.ben-manes.caffeine + caffeine + 3.0.4 + + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + + + com.fasterxml.jackson.core + jackson-databind + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + org.testcontainers + mongodb + 1.16.2 + test + + + + \ No newline at end of file diff --git a/spring-webflux-caching/src/main/java/com/baeldung/caching/Item.java b/spring-webflux-caching/src/main/java/com/baeldung/caching/Item.java new file mode 100644 index 0000000000..127975b0e7 --- /dev/null +++ b/spring-webflux-caching/src/main/java/com/baeldung/caching/Item.java @@ -0,0 +1,50 @@ +package com.baeldung.caching; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document +public class Item { + + @Id + private String _id; + private String name; + private double price; + + public Item(String name, double price) { + this.name = name; + this.price = price; + } + + public Item() { + } + + public String get_id() { + return _id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + @Override + public String toString() { + return "Item{" + + "id='" + _id + '\'' + + ", name='" + name + '\'' + + ", price=" + price + + '}'; + } +} diff --git a/spring-webflux-caching/src/main/java/com/baeldung/caching/ItemRepository.java b/spring-webflux-caching/src/main/java/com/baeldung/caching/ItemRepository.java new file mode 100644 index 0000000000..a76489623e --- /dev/null +++ b/spring-webflux-caching/src/main/java/com/baeldung/caching/ItemRepository.java @@ -0,0 +1,8 @@ +package com.baeldung.caching; + +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ItemRepository extends ReactiveMongoRepository { +} diff --git a/spring-webflux-caching/src/main/java/com/baeldung/caching/ItemService.java b/spring-webflux-caching/src/main/java/com/baeldung/caching/ItemService.java new file mode 100644 index 0000000000..9dc9ba1642 --- /dev/null +++ b/spring-webflux-caching/src/main/java/com/baeldung/caching/ItemService.java @@ -0,0 +1,42 @@ +package com.baeldung.caching; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import reactor.cache.CacheMono; +import reactor.core.publisher.Mono; + +@Service +public class ItemService { + + private final ItemRepository repository; + private final LoadingCache cache; + + public ItemService(ItemRepository repository) { + this.repository = repository; + this.cache = Caffeine.newBuilder() + .build(this::getItem_withAddons); + } + + @Cacheable("items") + public Mono getItem(String id) { + return repository.findById(id); + } + + public Mono save(Item item) { + return repository.save(item); + } + + @Cacheable("items") + public Mono getItem_withCache(String id) { + return repository.findById(id).cache(); + } + + @Cacheable("items") + public Mono getItem_withAddons(String id) { + return CacheMono.lookup(cache.asMap(), id) + .onCacheMissResume(() -> repository.findById(id).cast(Object.class)).cast(Item.class); + } + +} diff --git a/spring-webflux-caching/src/main/java/com/baeldung/caching/SpringWebfluxCachingApplication.java b/spring-webflux-caching/src/main/java/com/baeldung/caching/SpringWebfluxCachingApplication.java new file mode 100644 index 0000000000..7331576bd5 --- /dev/null +++ b/spring-webflux-caching/src/main/java/com/baeldung/caching/SpringWebfluxCachingApplication.java @@ -0,0 +1,16 @@ +package com.baeldung.caching; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; + +@SpringBootApplication +@EnableMongoRepositories +@EnableCaching +public class SpringWebfluxCachingApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringWebfluxCachingApplication.class, args); + } +} diff --git a/spring-webflux-caching/src/main/resources/application.properties b/spring-webflux-caching/src/main/resources/application.properties new file mode 100644 index 0000000000..23414da2dd --- /dev/null +++ b/spring-webflux-caching/src/main/resources/application.properties @@ -0,0 +1,2 @@ +logging.level.org.springframework.data.mongodb.core.ReactiveMongoTemplate=DEBUG +logging.level.org.springframework.cache=TRACE \ No newline at end of file diff --git a/spring-webflux-caching/src/test/java/com/baeldung/caching/MonoFluxResultCachingLiveTest.java b/spring-webflux-caching/src/test/java/com/baeldung/caching/MonoFluxResultCachingLiveTest.java new file mode 100644 index 0000000000..bf96b35dcb --- /dev/null +++ b/spring-webflux-caching/src/test/java/com/baeldung/caching/MonoFluxResultCachingLiveTest.java @@ -0,0 +1,95 @@ +package com.baeldung.caching; + + +import org.assertj.core.api.Assertions; +import org.junit.ClassRule; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.MongoDBContainer; +import org.testcontainers.utility.DockerImageName; +import reactor.core.publisher.Mono; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +public class MonoFluxResultCachingLiveTest { + + + @Autowired + ItemService itemService; + + final static MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")); + + @DynamicPropertySource + static void mongoDbProperties(DynamicPropertyRegistry registry) { + mongoDBContainer.start(); + registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl); + } + +@Test +public void givenItem_whenGetItemIsCalled_thenMonoIsCached() { + Mono glass = itemService.save(new Item("glass", 1.00)); + + String id = glass.block().get_id(); + + Mono mono = itemService.getItem(id); + Item item = mono.block(); + + assertThat(item).isNotNull(); + assertThat(item.getName()).isEqualTo("glass"); + assertThat(item.getPrice()).isEqualTo(1.00); + + Mono mono2 = itemService.getItem(id); + Item item2 = mono2.block(); + + assertThat(item2).isNotNull(); + assertThat(item2.getName()).isEqualTo("glass"); + assertThat(item2.getPrice()).isEqualTo(1.00); +} + + @Test + public void givenItem_whenGetItemWithCacheIsCalled_thenMonoResultIsCached() { + Mono glass = itemService.save(new Item("glass", 1.00)); + + String id = glass.block().get_id(); + + Mono mono = itemService.getItem_withCache(id); + Item item = mono.block(); + + assertThat(item).isNotNull(); + assertThat(item.getName()).isEqualTo("glass"); + assertThat(item.getPrice()).isEqualTo(1.00); + + Mono mono2 = itemService.getItem_withCache(id); + Item item2 = mono2.block(); + + assertThat(item2).isNotNull(); + assertThat(item2.getName()).isEqualTo("glass"); + assertThat(item2.getPrice()).isEqualTo(1.00); + } + + @Test + public void givenItem_whenGetItemWithAddonsIsCalled_thenMonoResultIsCached() { + Mono glass = itemService.save(new Item("glass", 1.00)); + + String id = glass.block().get_id(); + + Mono mono = itemService.getItem_withAddons(id); + Item item = mono.block(); + + assertThat(item).isNotNull(); + assertThat(item.getName()).isEqualTo("glass"); + assertThat(item.getPrice()).isEqualTo(1.00); + + Mono mono2 = itemService.getItem_withAddons(id); + Item item2 = mono2.block(); + + assertThat(item2).isNotNull(); + assertThat(item2.getName()).isEqualTo("glass"); + assertThat(item2.getPrice()).isEqualTo(1.00); + } + +}