Skip to content

Commit cb4222d

Browse files
committed
Documentation for caching with CompletableFuture and reactive types
See gh-17559 See gh-17920
1 parent 3437e61 commit cb4222d

File tree

1 file changed

+76
-16
lines changed

1 file changed

+76
-16
lines changed

framework-docs/modules/ROOT/pages/integration/cache/annotations.adoc

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ through its `key` attribute. You can use xref:core/expressions.adoc[SpEL] to pic
9898
arguments of interest (or their nested properties), perform operations, or even
9999
invoke arbitrary methods without having to write any code or implement any interface.
100100
This is the recommended approach over the
101-
xref:integration/cache/annotations.adoc#cache-annotations-cacheable-default-key[default generator], since methods tend to be
102-
quite different in signatures as the code base grows. While the default strategy might
103-
work for some methods, it rarely works for all methods.
101+
xref:integration/cache/annotations.adoc#cache-annotations-cacheable-default-key[default generator],
102+
since methods tend to be quite different in signatures as the code base grows. While the
103+
default strategy might work for some methods, it rarely works for all methods.
104104

105105
The following examples use various SpEL declarations (if you are not familiar with SpEL,
106106
do yourself a favor and read xref:core/expressions.adoc[Spring Expression Language]):
@@ -137,9 +137,8 @@ that specifies both results in an exception.
137137
[[cache-annotations-cacheable-default-cache-resolver]]
138138
=== Default Cache Resolution
139139

140-
The caching abstraction uses a simple `CacheResolver` that
141-
retrieves the caches defined at the operation level by using the configured
142-
`CacheManager`.
140+
The caching abstraction uses a simple `CacheResolver` that retrieves the caches
141+
defined at the operation level by using the configured `CacheManager`.
143142

144143
To provide a different default cache resolver, you need to implement the
145144
`org.springframework.cache.interceptor.CacheResolver` interface.
@@ -160,12 +159,11 @@ For applications that work with several cache managers, you can set the
160159
----
161160
<1> Specifying `anotherCacheManager`.
162161

163-
164162
You can also replace the `CacheResolver` entirely in a fashion similar to that of
165-
replacing xref:integration/cache/annotations.adoc#cache-annotations-cacheable-key[key generation]. The resolution is
166-
requested for every cache operation, letting the implementation actually resolve
167-
the caches to use based on runtime arguments. The following example shows how to
168-
specify a `CacheResolver`:
163+
replacing xref:integration/cache/annotations.adoc#cache-annotations-cacheable-key[key generation].
164+
The resolution is requested for every cache operation, letting the implementation
165+
actually resolve the caches to use based on runtime arguments. The following example
166+
shows how to specify a `CacheResolver`:
169167

170168
[source,java,indent=0,subs="verbatim,quotes"]
171169
----
@@ -174,7 +172,6 @@ specify a `CacheResolver`:
174172
----
175173
<1> Specifying the `CacheResolver`.
176174

177-
178175
[NOTE]
179176
====
180177
Since Spring 4.1, the `value` attribute of the cache annotations are no longer
@@ -211,6 +208,65 @@ NOTE: This is an optional feature, and your favorite cache library may not suppo
211208
All `CacheManager` implementations provided by the core framework support it. See the
212209
documentation of your cache provider for more details.
213210

211+
[[cache-annotations-cacheable-reactive]]
212+
=== Caching with CompletableFuture and Reactive Return Types
213+
214+
As of 6.1, cache annotations take `CompletableFuture` and reactive return types
215+
into account, automatically adapting the cache interaction accordingly.
216+
217+
For a method returning a `CompletableFuture`, the object produced by that future
218+
will be cached whenever it is complete, and the cache lookup for a cache hit will
219+
be retrieved via a `CompletableFuture`:
220+
221+
[source,java,indent=0,subs="verbatim,quotes"]
222+
----
223+
@Cacheable("books")
224+
public CompletableFuture<Book> findBook(ISBN isbn) {...}
225+
----
226+
227+
For a method returning a Reactor `Mono`, the object emitted by that Reactive Streams
228+
publisher will be cached whenever it is available, and the cache lookup for a cache
229+
hit will be retrieved as a `Mono` (backed by a `CompletableFuture`):
230+
231+
[source,java,indent=0,subs="verbatim,quotes"]
232+
----
233+
@Cacheable("books")
234+
public Mono<Book> findBook(ISBN isbn) {...}
235+
----
236+
237+
For a method returning a Reactor `Flux`, the objects emitted by that Reactive Streams
238+
publisher will be collected into a `List` and cached whenever that list is complete,
239+
and the cache lookup for a cache hit will be retrieved as a `Flux` (backed by a
240+
`CompletableFuture` for the cached `List` value):
241+
242+
[source,java,indent=0,subs="verbatim,quotes"]
243+
----
244+
@Cacheable("books")
245+
public Flux<Book> findBooks(String author) {...}
246+
----
247+
248+
Such `CompletableFuture` and reactive adaptation also works for synchronized caching,
249+
computing the value only once in case of a concurrent cache miss:
250+
251+
[source,java,indent=0,subs="verbatim,quotes"]
252+
----
253+
@Cacheable(cacheNames="foos", sync=true) <1>
254+
public CompletableFuture<Foo> executeExpensiveOperation(String id) {...}
255+
----
256+
<1> Using the `sync` attribute.
257+
258+
NOTE: In order for such an arrangement to work at runtime, the configured cache
259+
needs to be capable of `CompletableFuture`-based retrieval. The Spring-provided
260+
`ConcurrentMapCacheManager` automatically adapts to that retrieval style, and
261+
`CaffeineCacheManager` natively supports it when its asynchronous cache mode is
262+
enabled: set `setAsyncCacheMode(true)` on your `CaffeineCacheManager` instance.
263+
264+
Last but not least, be aware that annotation-driven caching is not appropriate
265+
for sophisticated reactive interactions involving composition and back pressure.
266+
If you choose to declare `@Cacheable` on specific reactive methods, consider the
267+
impact of the rather coarse-granular cache interaction which simply stores the
268+
emitted object for a `Mono` or even a pre-collected list of objects for a `Flux`.
269+
214270
[[cache-annotations-cacheable-condition]]
215271
=== Conditional Caching
216272

@@ -229,7 +285,6 @@ argument `name` has a length shorter than 32:
229285
----
230286
<1> Setting a condition on `@Cacheable`.
231287

232-
233288
In addition to the `condition` parameter, you can use the `unless` parameter to veto the
234289
adding of a value to the cache. Unlike `condition`, `unless` expressions are evaluated
235290
after the method has been invoked. To expand on the previous example, perhaps we only
@@ -242,7 +297,6 @@ want to cache paperback books, as the following example does:
242297
----
243298
<1> Using the `unless` attribute to block hardbacks.
244299

245-
246300
The cache abstraction supports `java.util.Optional` return types. If an `Optional` value
247301
is _present_, it will be stored in the associated cache. If an `Optional` value is not
248302
present, `null` will be stored in the associated cache. `#result` always refers to the
@@ -342,9 +396,12 @@ other), such declarations should be avoided. Note also that such conditions shou
342396
on the result object (that is, the `#result` variable), as these are validated up-front to
343397
confirm the exclusion.
344398

399+
As of 6.1, `@CachePut` takes `CompletableFuture` and reactive return types into account,
400+
performing the put operation whenever the produced object is available.
401+
345402

346403
[[cache-annotations-evict]]
347-
== The `@CacheEvict` annotation
404+
== The `@CacheEvict` Annotation
348405

349406
The cache abstraction allows not just population of a cache store but also eviction.
350407
This process is useful for removing stale or unused data from the cache. As opposed to
@@ -384,6 +441,9 @@ trigger, the return values are ignored (as they do not interact with the cache).
384441
not the case with `@Cacheable` which adds data to the cache or updates data in the cache
385442
and, thus, requires a result.
386443

444+
As of 6.1, `@CacheEvict` takes `CompletableFuture` and reactive return types into account,
445+
performing an after-invocation evict operation whenever processing has completed.
446+
387447

388448
[[cache-annotations-caching]]
389449
== The `@Caching` Annotation
@@ -402,7 +462,7 @@ The following example uses two `@CacheEvict` annotations:
402462

403463

404464
[[cache-annotations-config]]
405-
== The `@CacheConfig` annotation
465+
== The `@CacheConfig` Annotation
406466

407467
So far, we have seen that caching operations offer many customization options and that
408468
you can set these options for each operation. However, some of the customization options

0 commit comments

Comments
 (0)