22
22
import java .util .Map ;
23
23
import java .util .concurrent .ConcurrentHashMap ;
24
24
import java .util .concurrent .CopyOnWriteArrayList ;
25
+ import java .util .function .Supplier ;
25
26
27
+ import com .github .benmanes .caffeine .cache .AsyncCache ;
28
+ import com .github .benmanes .caffeine .cache .AsyncCacheLoader ;
26
29
import com .github .benmanes .caffeine .cache .CacheLoader ;
27
30
import com .github .benmanes .caffeine .cache .Caffeine ;
28
31
import com .github .benmanes .caffeine .cache .CaffeineSpec ;
45
48
* A {@link CaffeineSpec}-compliant expression value can also be applied
46
49
* via the {@link #setCacheSpecification "cacheSpecification"} bean property.
47
50
*
48
- * <p>Requires Caffeine 2.1 or higher.
51
+ * <p>Supports the {@link Cache#retrieve(Object)} and
52
+ * {@link Cache#retrieve(Object, Supplier)} operations through Caffeine's
53
+ * {@link AsyncCache}, when configured via {@link #setAsyncCacheMode}.
54
+ *
55
+ * <p>Requires Caffeine 3.0 or higher, as of Spring Framework 6.1.
49
56
*
50
57
* @author Ben Manes
51
58
* @author Juergen Hoeller
54
61
* @author Brian Clozel
55
62
* @since 4.3
56
63
* @see CaffeineCache
64
+ * @see #setCaffeineSpec
65
+ * @see #setCacheSpecification
66
+ * @see #setAsyncCacheMode
57
67
*/
58
68
public class CaffeineCacheManager implements CacheManager {
59
69
60
70
private Caffeine <Object , Object > cacheBuilder = Caffeine .newBuilder ();
61
71
62
72
@ Nullable
63
- private CacheLoader <Object , Object > cacheLoader ;
73
+ private AsyncCacheLoader <Object , Object > cacheLoader ;
74
+
75
+ private boolean asyncCacheMode = false ;
64
76
65
77
private boolean allowNullValues = true ;
66
78
@@ -110,7 +122,7 @@ public void setCacheNames(@Nullable Collection<String> cacheNames) {
110
122
* Set the Caffeine to use for building each individual
111
123
* {@link CaffeineCache} instance.
112
124
* @see #createNativeCaffeineCache
113
- * @see com.github.benmanes.caffeine.cache. Caffeine#build()
125
+ * @see Caffeine#build()
114
126
*/
115
127
public void setCaffeine (Caffeine <Object , Object > caffeine ) {
116
128
Assert .notNull (caffeine , "Caffeine must not be null" );
@@ -121,7 +133,7 @@ public void setCaffeine(Caffeine<Object, Object> caffeine) {
121
133
* Set the {@link CaffeineSpec} to use for building each individual
122
134
* {@link CaffeineCache} instance.
123
135
* @see #createNativeCaffeineCache
124
- * @see com.github.benmanes.caffeine.cache. Caffeine#from(CaffeineSpec)
136
+ * @see Caffeine#from(CaffeineSpec)
125
137
*/
126
138
public void setCaffeineSpec (CaffeineSpec caffeineSpec ) {
127
139
doSetCaffeine (Caffeine .from (caffeineSpec ));
@@ -132,7 +144,7 @@ public void setCaffeineSpec(CaffeineSpec caffeineSpec) {
132
144
* individual {@link CaffeineCache} instance. The given value needs to
133
145
* comply with Caffeine's {@link CaffeineSpec} (see its javadoc).
134
146
* @see #createNativeCaffeineCache
135
- * @see com.github.benmanes.caffeine.cache. Caffeine#from(String)
147
+ * @see Caffeine#from(String)
136
148
*/
137
149
public void setCacheSpecification (String cacheSpecification ) {
138
150
doSetCaffeine (Caffeine .from (cacheSpecification ));
@@ -149,7 +161,7 @@ private void doSetCaffeine(Caffeine<Object, Object> cacheBuilder) {
149
161
* Set the Caffeine CacheLoader to use for building each individual
150
162
* {@link CaffeineCache} instance, turning it into a LoadingCache.
151
163
* @see #createNativeCaffeineCache
152
- * @see com.github.benmanes.caffeine.cache. Caffeine#build(CacheLoader)
164
+ * @see Caffeine#build(CacheLoader)
153
165
* @see com.github.benmanes.caffeine.cache.LoadingCache
154
166
*/
155
167
public void setCacheLoader (CacheLoader <Object , Object > cacheLoader ) {
@@ -159,6 +171,45 @@ public void setCacheLoader(CacheLoader<Object, Object> cacheLoader) {
159
171
}
160
172
}
161
173
174
+ /**
175
+ * Set the Caffeine AsyncCacheLoader to use for building each individual
176
+ * {@link CaffeineCache} instance, turning it into a LoadingCache.
177
+ * <p>This implicitly switches the {@link #setAsyncCacheMode "asyncCacheMode"}
178
+ * flag to {@code true}.
179
+ * @since 6.1
180
+ * @see #createAsyncCaffeineCache
181
+ * @see Caffeine#buildAsync(AsyncCacheLoader)
182
+ * @see com.github.benmanes.caffeine.cache.LoadingCache
183
+ */
184
+ public void setAsyncCacheLoader (AsyncCacheLoader <Object , Object > cacheLoader ) {
185
+ if (!ObjectUtils .nullSafeEquals (this .cacheLoader , cacheLoader )) {
186
+ this .cacheLoader = cacheLoader ;
187
+ this .asyncCacheMode = true ;
188
+ refreshCommonCaches ();
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Set the common cache type that this cache manager builds to async.
194
+ * This applies to {@link #setCacheNames} as well as on-demand caches.
195
+ * <p>Individual cache registrations (such as {@link #registerCustomCache(String, AsyncCache)}
196
+ * and {@link #registerCustomCache(String, com.github.benmanes.caffeine.cache.Cache)}
197
+ * are not dependent on this setting.
198
+ * <p>By default, this cache manager builds regular native Caffeine caches.
199
+ * To switch to async caches which can also be used through the synchronous API
200
+ * but come with support for {@code Cache#retrieve}, set this flag to {@code true}.
201
+ * @since 6.1
202
+ * @see Caffeine#buildAsync()
203
+ * @see Cache#retrieve(Object)
204
+ * @see Cache#retrieve(Object, Supplier)
205
+ */
206
+ public void setAsyncCacheMode (boolean asyncCacheMode ) {
207
+ if (this .asyncCacheMode != asyncCacheMode ) {
208
+ this .asyncCacheMode = asyncCacheMode ;
209
+ refreshCommonCaches ();
210
+ }
211
+ }
212
+
162
213
/**
163
214
* Specify whether to accept and convert {@code null} values for all caches
164
215
* in this cache manager.
@@ -211,27 +262,62 @@ public Cache getCache(String name) {
211
262
* @param name the name of the cache
212
263
* @param cache the custom Caffeine Cache instance to register
213
264
* @since 5.2.8
214
- * @see #adaptCaffeineCache
265
+ * @see #adaptCaffeineCache(String, com.github.benmanes.caffeine.cache.Cache)
215
266
*/
216
267
public void registerCustomCache (String name , com .github .benmanes .caffeine .cache .Cache <Object , Object > cache ) {
217
268
this .customCacheNames .add (name );
218
269
this .cacheMap .put (name , adaptCaffeineCache (name , cache ));
219
270
}
220
271
272
+ /**
273
+ * Register the given Caffeine AsyncCache instance with this cache manager,
274
+ * adapting it to Spring's cache API for exposure through {@link #getCache}.
275
+ * Any number of such custom caches may be registered side by side.
276
+ * <p>This allows for custom settings per cache (as opposed to all caches
277
+ * sharing the common settings in the cache manager's configuration) and
278
+ * is typically used with the Caffeine builder API:
279
+ * {@code registerCustomCache("myCache", Caffeine.newBuilder().maximumSize(10).build())}
280
+ * <p>Note that any other caches, whether statically specified through
281
+ * {@link #setCacheNames} or dynamically built on demand, still operate
282
+ * with the common settings in the cache manager's configuration.
283
+ * @param name the name of the cache
284
+ * @param cache the custom Caffeine Cache instance to register
285
+ * @since 6.1
286
+ * @see #adaptCaffeineCache(String, AsyncCache)
287
+ */
288
+ public void registerCustomCache (String name , AsyncCache <Object , Object > cache ) {
289
+ this .customCacheNames .add (name );
290
+ this .cacheMap .put (name , adaptCaffeineCache (name , cache ));
291
+ }
292
+
221
293
/**
222
294
* Adapt the given new native Caffeine Cache instance to Spring's {@link Cache}
223
295
* abstraction for the specified cache name.
224
296
* @param name the name of the cache
225
297
* @param cache the native Caffeine Cache instance
226
298
* @return the Spring CaffeineCache adapter (or a decorator thereof)
227
299
* @since 5.2.8
228
- * @see CaffeineCache
300
+ * @see CaffeineCache#CaffeineCache(String, com.github.benmanes.caffeine.cache.Cache, boolean)
229
301
* @see #isAllowNullValues()
230
302
*/
231
303
protected Cache adaptCaffeineCache (String name , com .github .benmanes .caffeine .cache .Cache <Object , Object > cache ) {
232
304
return new CaffeineCache (name , cache , isAllowNullValues ());
233
305
}
234
306
307
+ /**
308
+ * Adapt the given new Caffeine AsyncCache instance to Spring's {@link Cache}
309
+ * abstraction for the specified cache name.
310
+ * @param name the name of the cache
311
+ * @param cache the Caffeine AsyncCache instance
312
+ * @return the Spring CaffeineCache adapter (or a decorator thereof)
313
+ * @since 6.1
314
+ * @see CaffeineCache#CaffeineCache(String, AsyncCache, boolean)
315
+ * @see #isAllowNullValues()
316
+ */
317
+ protected Cache adaptCaffeineCache (String name , AsyncCache <Object , Object > cache ) {
318
+ return new CaffeineCache (name , cache , isAllowNullValues ());
319
+ }
320
+
235
321
/**
236
322
* Build a common {@link CaffeineCache} instance for the specified cache name,
237
323
* using the common Caffeine configuration specified on this cache manager.
@@ -244,7 +330,8 @@ protected Cache adaptCaffeineCache(String name, com.github.benmanes.caffeine.cac
244
330
* @see #createNativeCaffeineCache
245
331
*/
246
332
protected Cache createCaffeineCache (String name ) {
247
- return adaptCaffeineCache (name , createNativeCaffeineCache (name ));
333
+ return (this .asyncCacheMode ? adaptCaffeineCache (name , createAsyncCaffeineCache (name )) :
334
+ adaptCaffeineCache (name , createNativeCaffeineCache (name )));
248
335
}
249
336
250
337
/**
@@ -255,7 +342,29 @@ protected Cache createCaffeineCache(String name) {
255
342
* @see #createCaffeineCache
256
343
*/
257
344
protected com .github .benmanes .caffeine .cache .Cache <Object , Object > createNativeCaffeineCache (String name ) {
258
- return (this .cacheLoader != null ? this .cacheBuilder .build (this .cacheLoader ) : this .cacheBuilder .build ());
345
+ if (this .cacheLoader != null ) {
346
+ if (this .cacheLoader instanceof CacheLoader <Object , Object > regularCacheLoader ) {
347
+ return this .cacheBuilder .build (regularCacheLoader );
348
+ }
349
+ else {
350
+ throw new IllegalStateException (
351
+ "Cannot create regular Caffeine Cache with async-only cache loader: " + this .cacheLoader );
352
+ }
353
+ }
354
+ return this .cacheBuilder .build ();
355
+ }
356
+
357
+ /**
358
+ * Build a common Caffeine AsyncCache instance for the specified cache name,
359
+ * using the common Caffeine configuration specified on this cache manager.
360
+ * @param name the name of the cache
361
+ * @return the Caffeine AsyncCache instance
362
+ * @since 6.1
363
+ * @see #createCaffeineCache
364
+ */
365
+ protected AsyncCache <Object , Object > createAsyncCaffeineCache (String name ) {
366
+ return (this .cacheLoader != null ? this .cacheBuilder .buildAsync (this .cacheLoader ) :
367
+ this .cacheBuilder .buildAsync ());
259
368
}
260
369
261
370
/**
0 commit comments