@@ -98,9 +98,9 @@ through its `key` attribute. You can use xref:core/expressions.adoc[SpEL] to pic
98
98
arguments of interest (or their nested properties), perform operations, or even
99
99
invoke arbitrary methods without having to write any code or implement any interface.
100
100
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.
104
104
105
105
The following examples use various SpEL declarations (if you are not familiar with SpEL,
106
106
do yourself a favor and read xref:core/expressions.adoc[Spring Expression Language]):
@@ -137,9 +137,8 @@ that specifies both results in an exception.
137
137
[[cache-annotations-cacheable-default-cache-resolver]]
138
138
=== Default Cache Resolution
139
139
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`.
143
142
144
143
To provide a different default cache resolver, you need to implement the
145
144
`org.springframework.cache.interceptor.CacheResolver` interface.
@@ -160,12 +159,11 @@ For applications that work with several cache managers, you can set the
160
159
----
161
160
<1> Specifying `anotherCacheManager`.
162
161
163
-
164
162
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`:
169
167
170
168
[source,java,indent=0,subs="verbatim,quotes"]
171
169
----
@@ -174,7 +172,6 @@ specify a `CacheResolver`:
174
172
----
175
173
<1> Specifying the `CacheResolver`.
176
174
177
-
178
175
[NOTE]
179
176
====
180
177
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
211
208
All `CacheManager` implementations provided by the core framework support it. See the
212
209
documentation of your cache provider for more details.
213
210
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
+
214
270
[[cache-annotations-cacheable-condition]]
215
271
=== Conditional Caching
216
272
@@ -229,7 +285,6 @@ argument `name` has a length shorter than 32:
229
285
----
230
286
<1> Setting a condition on `@Cacheable`.
231
287
232
-
233
288
In addition to the `condition` parameter, you can use the `unless` parameter to veto the
234
289
adding of a value to the cache. Unlike `condition`, `unless` expressions are evaluated
235
290
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:
242
297
----
243
298
<1> Using the `unless` attribute to block hardbacks.
244
299
245
-
246
300
The cache abstraction supports `java.util.Optional` return types. If an `Optional` value
247
301
is _present_, it will be stored in the associated cache. If an `Optional` value is not
248
302
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
342
396
on the result object (that is, the `#result` variable), as these are validated up-front to
343
397
confirm the exclusion.
344
398
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
+
345
402
346
403
[[cache-annotations-evict]]
347
- == The `@CacheEvict` annotation
404
+ == The `@CacheEvict` Annotation
348
405
349
406
The cache abstraction allows not just population of a cache store but also eviction.
350
407
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).
384
441
not the case with `@Cacheable` which adds data to the cache or updates data in the cache
385
442
and, thus, requires a result.
386
443
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
+
387
447
388
448
[[cache-annotations-caching]]
389
449
== The `@Caching` Annotation
@@ -402,7 +462,7 @@ The following example uses two `@CacheEvict` annotations:
402
462
403
463
404
464
[[cache-annotations-config]]
405
- == The `@CacheConfig` annotation
465
+ == The `@CacheConfig` Annotation
406
466
407
467
So far, we have seen that caching operations offer many customization options and that
408
468
you can set these options for each operation. However, some of the customization options
0 commit comments