0
# Caching
1
2
Declarative caching abstraction providing transparent caching capabilities for method return values. This includes @Cacheable, @CachePut, and @CacheEvict annotations, cache managers, key generation strategies, and error handling for building high-performance applications with caching.
3
4
## Capabilities
5
6
### Caching Annotations
7
8
Declarative caching through annotations that automatically cache method return values and manage cache entries.
9
10
```java { .api }
11
/**
12
* Annotation indicating that the result of invoking a method (or all methods in a class) can be cached.
13
*/
14
@Target({ElementType.TYPE, ElementType.METHOD})
15
@Retention(RetentionPolicy.RUNTIME)
16
@Inherited
17
public @interface Cacheable {
18
/**
19
* Alias for cacheNames().
20
* @return the cache names
21
*/
22
@AliasFor("cacheNames")
23
String[] value() default {};
24
25
/**
26
* Names of the caches in which method invocation results are stored.
27
* @return the cache names
28
*/
29
@AliasFor("value")
30
String[] cacheNames() default {};
31
32
/**
33
* Spring Expression Language (SpEL) expression for computing the key dynamically.
34
* @return the key expression
35
*/
36
String key() default "";
37
38
/**
39
* The bean name of the custom KeyGenerator to use.
40
* @return the key generator bean name
41
*/
42
String keyGenerator() default "";
43
44
/**
45
* The bean name of the custom CacheManager to use to create a default CacheResolver if none is set already.
46
* @return the cache manager bean name
47
*/
48
String cacheManager() default "";
49
50
/**
51
* The bean name of the custom CacheResolver to use.
52
* @return the cache resolver bean name
53
*/
54
String cacheResolver() default "";
55
56
/**
57
* Spring Expression Language (SpEL) expression used for making the method caching conditional.
58
* @return the condition expression
59
*/
60
String condition() default "";
61
62
/**
63
* Spring Expression Language (SpEL) expression used to veto method caching.
64
* @return the unless expression
65
*/
66
String unless() default "";
67
68
/**
69
* Synchronize the invocation of the underlying method if several threads are attempting to load a value for the same key.
70
* @return whether to synchronize cache access
71
*/
72
boolean sync() default false;
73
}
74
75
/**
76
* Annotation indicating that a method (or all methods on a class) trigger a cache put operation.
77
*/
78
@Target({ElementType.TYPE, ElementType.METHOD})
79
@Retention(RetentionPolicy.RUNTIME)
80
@Inherited
81
public @interface CachePut {
82
/**
83
* Alias for cacheNames().
84
* @return the cache names
85
*/
86
@AliasFor("cacheNames")
87
String[] value() default {};
88
89
/**
90
* Names of the caches in which method invocation results are stored.
91
* @return the cache names
92
*/
93
@AliasFor("value")
94
String[] cacheNames() default {};
95
96
/**
97
* Spring Expression Language (SpEL) expression for computing the key dynamically.
98
* @return the key expression
99
*/
100
String key() default "";
101
102
/**
103
* The bean name of the custom KeyGenerator to use.
104
* @return the key generator bean name
105
*/
106
String keyGenerator() default "";
107
108
/**
109
* The bean name of the custom CacheManager to use to create a default CacheResolver if none is set already.
110
* @return the cache manager bean name
111
*/
112
String cacheManager() default "";
113
114
/**
115
* The bean name of the custom CacheResolver to use.
116
* @return the cache resolver bean name
117
*/
118
String cacheResolver() default "";
119
120
/**
121
* Spring Expression Language (SpEL) expression used for making the method caching conditional.
122
* @return the condition expression
123
*/
124
String condition() default "";
125
126
/**
127
* Spring Expression Language (SpEL) expression used to veto method caching.
128
* @return the unless expression
129
*/
130
String unless() default "";
131
}
132
133
/**
134
* Annotation indicating that a method (or all methods on a class) trigger cache evict operations.
135
*/
136
@Target({ElementType.TYPE, ElementType.METHOD})
137
@Retention(RetentionPolicy.RUNTIME)
138
@Inherited
139
public @interface CacheEvict {
140
/**
141
* Alias for cacheNames().
142
* @return the cache names
143
*/
144
@AliasFor("cacheNames")
145
String[] value() default {};
146
147
/**
148
* Names of the caches to evict.
149
* @return the cache names
150
*/
151
@AliasFor("value")
152
String[] cacheNames() default {};
153
154
/**
155
* Spring Expression Language (SpEL) expression for computing the key dynamically.
156
* @return the key expression
157
*/
158
String key() default "";
159
160
/**
161
* The bean name of the custom KeyGenerator to use.
162
* @return the key generator bean name
163
*/
164
String keyGenerator() default "";
165
166
/**
167
* The bean name of the custom CacheManager to use to create a default CacheResolver if none is set already.
168
* @return the cache manager bean name
169
*/
170
String cacheManager() default "";
171
172
/**
173
* The bean name of the custom CacheResolver to use.
174
* @return the cache resolver bean name
175
*/
176
String cacheResolver() default "";
177
178
/**
179
* Spring Expression Language (SpEL) expression used for making the cache eviction conditional.
180
* @return the condition expression
181
*/
182
String condition() default "";
183
184
/**
185
* Whether all the entries inside the cache(s) are removed.
186
* @return whether to evict all entries
187
*/
188
boolean allEntries() default false;
189
190
/**
191
* Whether the eviction should occur before the method is invoked.
192
* @return whether to evict before invocation
193
*/
194
boolean beforeInvocation() default false;
195
}
196
197
/**
198
* Group annotation for multiple cache annotations (of different or the same type).
199
*/
200
@Target({ElementType.TYPE, ElementType.METHOD})
201
@Retention(RetentionPolicy.RUNTIME)
202
@Inherited
203
public @interface Caching {
204
/**
205
* Array of @Cacheable annotations.
206
* @return the cacheable annotations
207
*/
208
Cacheable[] cacheable() default {};
209
210
/**
211
* Array of @CachePut annotations.
212
* @return the cache put annotations
213
*/
214
CachePut[] put() default {};
215
216
/**
217
* Array of @CacheEvict annotations.
218
* @return the cache evict annotations
219
*/
220
CacheEvict[] evict() default {};
221
}
222
223
/**
224
* @CacheConfig provides a mechanism for sharing common cache-related settings at the class level.
225
*/
226
@Target(ElementType.TYPE)
227
@Retention(RetentionPolicy.RUNTIME)
228
public @interface CacheConfig {
229
/**
230
* Names of the default caches to consider for caching operations defined in this class.
231
* @return the cache names
232
*/
233
String[] cacheNames() default {};
234
235
/**
236
* The bean name of the default KeyGenerator to use for the class.
237
* @return the key generator bean name
238
*/
239
String keyGenerator() default "";
240
241
/**
242
* The bean name of the custom CacheManager to use to create a default CacheResolver if none is set already.
243
* @return the cache manager bean name
244
*/
245
String cacheManager() default "";
246
247
/**
248
* The bean name of the custom CacheResolver to use.
249
* @return the cache resolver bean name
250
*/
251
String cacheResolver() default "";
252
}
253
254
/**
255
* Enables Spring's annotation-driven cache management capability, similar to the support found in Spring's XML namespace.
256
*/
257
@Target(ElementType.TYPE)
258
@Retention(RetentionPolicy.RUNTIME)
259
@Import(CachingConfigurationSelector.class)
260
public @interface EnableCaching {
261
/**
262
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to standard Java interface-based proxies.
263
* @return whether to proxy the target class
264
*/
265
boolean proxyTargetClass() default false;
266
267
/**
268
* Indicate how caching advice should be applied.
269
* @return the advice mode
270
*/
271
AdviceMode mode() default AdviceMode.PROXY;
272
273
/**
274
* Indicate the ordering of the execution of the caching advisor when multiple advices are applied at a specific join point.
275
* @return the order for the caching advisor
276
*/
277
int order() default Ordered.LOWEST_PRECEDENCE;
278
}
279
```
280
281
### Cache Abstraction Interfaces
282
283
Core interfaces defining the cache abstraction layer.
284
285
```java { .api }
286
/**
287
* Interface that defines common cache operations.
288
* Note: Due to the generic use of caching, it is recommended that implementations allow storage of null values.
289
*/
290
public interface Cache {
291
/**
292
* Return the cache name.
293
* @return the cache name
294
*/
295
String getName();
296
297
/**
298
* Return the underlying native cache provider.
299
* @return the native cache provider
300
*/
301
Object getNativeCache();
302
303
/**
304
* Return the value to which this cache maps the specified key.
305
* @param key the key whose associated value is to be returned
306
* @return the value to which this cache maps the specified key, or null if the cache contains no mapping for this key
307
*/
308
ValueWrapper get(Object key);
309
310
/**
311
* Return the value to which this cache maps the specified key, generically specifying a type that return value will be cast to.
312
* @param key the key whose associated value is to be returned
313
* @param type the required type of the returned value (may be null to bypass a type check)
314
* @return the value to which this cache maps the specified key (which may be null itself), or also null if the cache contains no mapping for this key
315
* @throws IllegalStateException if a cache entry has been found but failed to match the specified type
316
*/
317
<T> T get(Object key, Class<T> type);
318
319
/**
320
* Return the value to which this cache maps the specified key, obtaining that value from valueLoader if necessary.
321
* @param key the key whose associated value is to be returned
322
* @param valueLoader the value loader which will compute the value if necessary
323
* @return the value to which this cache maps the specified key
324
* @throws ValueRetrievalException if the valueLoader throws an exception or returns a null value
325
*/
326
<T> T get(Object key, Callable<T> valueLoader);
327
328
/**
329
* Associate the specified value with the specified key in this cache.
330
* @param key the key with which the specified value is to be associated
331
* @param value the value to be associated with the specified key
332
*/
333
void put(Object key, Object value);
334
335
/**
336
* Atomically associate the specified value with the specified key in this cache if it is not set already.
337
* @param key the key with which the specified value is to be associated
338
* @param value the value to be associated with the specified key
339
* @return the value that was associated with the key, or null if none was
340
*/
341
default ValueWrapper putIfAbsent(Object key, Object value) {
342
ValueWrapper existingValue = get(key);
343
if (existingValue == null) {
344
put(key, value);
345
}
346
return existingValue;
347
}
348
349
/**
350
* Evict the mapping for this key from this cache if it is present.
351
* @param key the key whose mapping is to be removed from the cache
352
*/
353
void evict(Object key);
354
355
/**
356
* Evict the mapping for this key from this cache if it is present.
357
* @param key the key whose mapping is to be removed from the cache
358
* @return true if the key was present, false if there was no mapping for the key
359
*/
360
default boolean evictIfPresent(Object key) {
361
evict(key);
362
return false;
363
}
364
365
/**
366
* Clear the cache through removing all mappings.
367
*/
368
void clear();
369
370
/**
371
* Invalidate the cache through removing all mappings.
372
* @return true if the cache was cleared, false if there was no cache entry that could be cleared or if the cache is not active
373
*/
374
default boolean invalidate() {
375
clear();
376
return true;
377
}
378
379
/**
380
* A (wrapper) object representing a cache value.
381
*/
382
@FunctionalInterface
383
interface ValueWrapper {
384
/**
385
* Return the actual value in the cache.
386
* @return the actual value in the cache (may be null)
387
*/
388
Object get();
389
}
390
391
/**
392
* Wrapper exception to be thrown from get(Object, Callable) in case of the value loader callback failing with an exception.
393
*/
394
class ValueRetrievalException extends RuntimeException {
395
private final Object key;
396
397
public ValueRetrievalException(Object key, Callable<?> loader, Throwable ex) {}
398
399
public Object getKey() {}
400
}
401
}
402
403
/**
404
* Spring's central cache manager SPI.
405
* Allows for retrieving named Cache regions.
406
*/
407
public interface CacheManager {
408
/**
409
* Get the cache associated with the given name.
410
* @param name the cache identifier (must not be null)
411
* @return the associated cache, or null if such a cache does not exist or could be not created
412
*/
413
Cache getCache(String name);
414
415
/**
416
* Get a collection of the cache names known by this manager.
417
* @return the names of all caches known by the cache manager
418
*/
419
Collection<String> getCacheNames();
420
}
421
```
422
423
### Cache Configuration Support
424
425
Interfaces and classes for configuring caching behavior.
426
427
```java { .api }
428
/**
429
* Interface to be implemented by @Configuration classes annotated with @EnableCaching
430
* that wish to (or need to) specify explicitly how caches are resolved and how keys are generated for annotation-driven cache management.
431
*/
432
public interface CachingConfigurer {
433
/**
434
* Return the cache manager bean to use for annotation-driven cache management.
435
* @return the cache manager instance
436
*/
437
default CacheManager cacheManager() {
438
return null;
439
}
440
441
/**
442
* Return the CacheResolver bean to use to resolve regular caches for annotation-driven cache management.
443
* @return the cache resolver instance
444
*/
445
default CacheResolver cacheResolver() {
446
return null;
447
}
448
449
/**
450
* Return the key generator bean to use for annotation-driven cache management.
451
* @return the key generator instance
452
*/
453
default KeyGenerator keyGenerator() {
454
return null;
455
}
456
457
/**
458
* Return the CacheErrorHandler to use to handle cache-related errors.
459
* @return the error handler instance
460
*/
461
default CacheErrorHandler errorHandler() {
462
return null;
463
}
464
}
465
466
/**
467
* An implementation of CachingConfigurer with empty methods allowing sub-classes to override only the methods they're interested in.
468
*/
469
public class CachingConfigurerSupport implements CachingConfigurer {
470
/**
471
* Return the cache manager bean to use for annotation-driven cache management.
472
* @return null
473
*/
474
@Override
475
public CacheManager cacheManager() {
476
return null;
477
}
478
479
/**
480
* Return the CacheResolver bean to use to resolve regular caches for annotation-driven cache management.
481
* @return null
482
*/
483
@Override
484
public CacheResolver cacheResolver() {
485
return null;
486
}
487
488
/**
489
* Return the key generator bean to use for annotation-driven cache management.
490
* @return null
491
*/
492
@Override
493
public KeyGenerator keyGenerator() {
494
return null;
495
}
496
497
/**
498
* Return the CacheErrorHandler to use to handle cache-related errors.
499
* @return null
500
*/
501
@Override
502
public CacheErrorHandler errorHandler() {
503
return null;
504
}
505
}
506
```
507
508
### Key Generation
509
510
Interfaces and classes for generating cache keys.
511
512
```java { .api }
513
/**
514
* Cache key generator. Used for creating a key based on the given method (used as context) and its parameters.
515
*/
516
@FunctionalInterface
517
public interface KeyGenerator {
518
/**
519
* Generate a key for the given method and its parameters.
520
* @param target the target instance
521
* @param method the method being called
522
* @param params the method parameters (with any var-args expanded)
523
* @return a generated key
524
*/
525
Object generate(Object target, Method method, Object... params);
526
}
527
528
/**
529
* Simple key generator. Returns the parameter itself if a single non-null value is given,
530
* otherwise returns a SimpleKey of the parameters.
531
*/
532
public class SimpleKeyGenerator implements KeyGenerator {
533
/**
534
* Generate a key for the given method and its parameters.
535
* @param target the target instance
536
* @param method the method being called
537
* @param params the method parameters (with any var-args expanded)
538
* @return a generated key
539
*/
540
public Object generate(Object target, Method method, Object... params) {}
541
}
542
543
/**
544
* A simple key as returned by the SimpleKeyGenerator.
545
*/
546
public class SimpleKey implements Serializable {
547
/** The hash code of the key */
548
private final int hashCode;
549
550
/** The parameters that make up the key */
551
private final Object[] params;
552
553
/**
554
* Create a new SimpleKey instance.
555
* @param elements the elements of the key
556
*/
557
public SimpleKey(Object... elements) {}
558
559
/**
560
* Check equality with another object.
561
* @param other the object to compare with
562
* @return true if equal, false otherwise
563
*/
564
@Override
565
public boolean equals(Object other) {}
566
567
/**
568
* Return the hash code for this key.
569
* @return the hash code
570
*/
571
@Override
572
public int hashCode() {}
573
574
/**
575
* Return a string representation of this key.
576
* @return a string representation
577
*/
578
@Override
579
public String toString() {}
580
}
581
```
582
583
### Cache Resolution
584
585
Interfaces and classes for resolving caches based on operation context.
586
587
```java { .api }
588
/**
589
* Determine the Cache instance(s) to use for an intercepted method invocation.
590
*/
591
@FunctionalInterface
592
public interface CacheResolver {
593
/**
594
* Return the cache(s) to use for the specified invocation.
595
* @param context the context of the cache operation
596
* @return the cache(s) to use (never null)
597
* @throws IllegalStateException if cache resolution failed
598
*/
599
Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
600
}
601
602
/**
603
* A simple CacheResolver that resolves the Cache instance(s) based on a configurable CacheManager and the name of the cache(s) as provided by getCacheNames().
604
*/
605
public class SimpleCacheResolver extends AbstractCacheResolver {
606
/**
607
* Construct a new SimpleCacheResolver.
608
*/
609
public SimpleCacheResolver() {}
610
611
/**
612
* Construct a new SimpleCacheResolver for the given CacheManager.
613
* @param cacheManager the CacheManager to use
614
*/
615
public SimpleCacheResolver(CacheManager cacheManager) {}
616
}
617
618
/**
619
* A CacheResolver that resolves the caches based on their names.
620
*/
621
public class NamedCacheResolver extends AbstractCacheResolver {
622
private final Collection<String> cacheNames;
623
624
/**
625
* Construct a new NamedCacheResolver for the given CacheManager and cache name(s).
626
* @param cacheManager the CacheManager to use
627
* @param cacheNames the cache name(s) to resolve
628
*/
629
public NamedCacheResolver(CacheManager cacheManager, String... cacheNames) {}
630
631
/**
632
* Set the cache name(s) that this resolver should use.
633
* @param cacheNames the cache name(s)
634
*/
635
public void setCacheNames(Collection<String> cacheNames) {}
636
}
637
```
638
639
### Cache Manager Implementations
640
641
Concrete implementations of CacheManager for different caching providers.
642
643
```java { .api }
644
/**
645
* Simple cache manager working against a given collection of caches.
646
*/
647
public class SimpleCacheManager extends AbstractCacheManager {
648
private Collection<? extends Cache> caches = Collections.emptySet();
649
650
/**
651
* Specify the collection of Cache instances to use for this CacheManager.
652
* @param caches the Cache instances to use
653
*/
654
public void setCaches(Collection<? extends Cache> caches) {}
655
656
/**
657
* Load the initial caches for this cache manager.
658
* @return the collection of caches
659
*/
660
protected Collection<? extends Cache> loadCaches() {}
661
}
662
663
/**
664
* Composite CacheManager implementation that iterates over a given collection of delegate CacheManager instances.
665
*/
666
public class CompositeCacheManager implements CacheManager, InitializingBean {
667
private final List<CacheManager> cacheManagers = new ArrayList<>();
668
private boolean fallbackToNoOpCache = false;
669
670
/**
671
* Construct a CompositeCacheManager.
672
*/
673
public CompositeCacheManager() {}
674
675
/**
676
* Construct a CompositeCacheManager from the given delegate CacheManagers.
677
* @param cacheManagers the CacheManagers to delegate to
678
*/
679
public CompositeCacheManager(CacheManager... cacheManagers) {}
680
681
/**
682
* Set the CacheManagers to delegate to.
683
* @param cacheManagers the CacheManagers to delegate to
684
*/
685
public void setCacheManagers(CacheManager... cacheManagers) {}
686
687
/**
688
* Indicate whether a NoOpCache should be returned when no matching cache is found.
689
* @param fallbackToNoOpCache whether to fall back to NoOpCache
690
*/
691
public void setFallbackToNoOpCache(boolean fallbackToNoOpCache) {}
692
693
/**
694
* Get the cache with the specified name from the first CacheManager that contains it.
695
* @param name the cache name
696
* @return the Cache instance, or null if not found
697
*/
698
public Cache getCache(String name) {}
699
700
/**
701
* Get all cache names from all delegate CacheManagers.
702
* @return the collection of cache names
703
*/
704
public Collection<String> getCacheNames() {}
705
}
706
707
/**
708
* A basic, no operation CacheManager implementation suitable for disabling caching,
709
* typically used for backing cache declarations without an actual backing store.
710
*/
711
public class NoOpCacheManager implements CacheManager {
712
private final Set<String> cacheNames = ConcurrentHashMap.newKeySet(16);
713
714
/**
715
* This implementation always returns a NoOpCache instance.
716
* @param name the cache name
717
* @return a NoOpCache instance
718
*/
719
public Cache getCache(String name) {}
720
721
/**
722
* Return the cache names that have been requested so far.
723
* @return the collection of cache names
724
*/
725
public Collection<String> getCacheNames() {}
726
}
727
728
/**
729
* CacheManager implementation that lazily builds ConcurrentMapCache instances for each getCache(String) request.
730
*/
731
public class ConcurrentMapCacheManager extends AbstractCacheManager {
732
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
733
private boolean allowNullValues = true;
734
private boolean storeByValue = false;
735
736
/**
737
* Construct a dynamic ConcurrentMapCacheManager, lazily creating cache instances as they are being requested.
738
*/
739
public ConcurrentMapCacheManager() {}
740
741
/**
742
* Construct a static ConcurrentMapCacheManager, managing caches for the specified cache names only.
743
* @param cacheNames the names of the static cache to create
744
*/
745
public ConcurrentMapCacheManager(String... cacheNames) {}
746
747
/**
748
* Specify the set of cache names for this CacheManager's 'static' mode.
749
* @param cacheNames the names of the static cache to create
750
*/
751
public void setCacheNames(Collection<String> cacheNames) {}
752
753
/**
754
* Specify whether to accept and convert null values for all caches in this cache manager.
755
* @param allowNullValues whether to allow null values
756
*/
757
public void setAllowNullValues(boolean allowNullValues) {}
758
759
/**
760
* Specify whether this cache manager stores a copy of each entry (true) or a reference (false) for all of its caches.
761
* @param storeByValue whether to store by value vs by reference
762
*/
763
public void setStoreByValue(boolean storeByValue) {}
764
765
/**
766
* Load the initial caches for this cache manager.
767
* @return the collection of caches
768
*/
769
protected Collection<Cache> loadCaches() {}
770
771
/**
772
* Dynamically register an additional Cache with this manager.
773
* @param cache the Cache to register
774
*/
775
public void registerCustomCache(Cache cache) {}
776
}
777
```
778
779
### Error Handling
780
781
Interfaces and classes for handling caching errors.
782
783
```java { .api }
784
/**
785
* A strategy for handling cache-related errors. In most cases, any exception thrown by the provider should simply be thrown back at the client.
786
* However, for some cases it might be useful to handle cache provider exceptions in a different way.
787
*/
788
public interface CacheErrorHandler {
789
/**
790
* Handle the given runtime exception thrown by the cache provider when retrieving an item with the specified key,
791
* possibly rethrowing it as a fatal exception.
792
* @param exception the exception thrown by the cache provider
793
* @param cache the cache
794
* @param key the key used to get the item
795
*/
796
void handleCacheGetError(RuntimeException exception, Cache cache, Object key);
797
798
/**
799
* Handle the given runtime exception thrown by the cache provider when updating an item with the specified key and value,
800
* possibly rethrowing it as a fatal exception.
801
* @param exception the exception thrown by the cache provider
802
* @param cache the cache
803
* @param key the key used to update the item
804
* @param value the value used to update the item
805
*/
806
void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value);
807
808
/**
809
* Handle the given runtime exception thrown by the cache provider when clearing an item with the specified key,
810
* possibly rethrowing it as a fatal exception.
811
* @param exception the exception thrown by the cache provider
812
* @param cache the cache
813
* @param key the key used to clear the item
814
*/
815
void handleCacheEvictError(RuntimeException exception, Cache cache, Object key);
816
817
/**
818
* Handle the given runtime exception thrown by the cache provider when clearing the specified cache,
819
* possibly rethrowing it as a fatal exception.
820
* @param exception the exception thrown by the cache provider
821
* @param cache the cache to clear
822
*/
823
void handleCacheClearError(RuntimeException exception, Cache cache);
824
}
825
826
/**
827
* Simple CacheErrorHandler that does not handle any exception at all, simply throwing it back at the client.
828
*/
829
public class SimpleCacheErrorHandler implements CacheErrorHandler {
830
/**
831
* Simply throw the exception as-is.
832
* @param exception the exception thrown by the cache provider
833
* @param cache the cache
834
* @param key the key used to get the item
835
*/
836
@Override
837
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {}
838
839
/**
840
* Simply throw the exception as-is.
841
* @param exception the exception thrown by the cache provider
842
* @param cache the cache
843
* @param key the key used to update the item
844
* @param value the value used to update the item
845
*/
846
@Override
847
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {}
848
849
/**
850
* Simply throw the exception as-is.
851
* @param exception the exception thrown by the cache provider
852
* @param cache the cache
853
* @param key the key used to clear the item
854
*/
855
@Override
856
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {}
857
858
/**
859
* Simply throw the exception as-is.
860
* @param exception the exception thrown by the cache provider
861
* @param cache the cache to clear
862
*/
863
@Override
864
public void handleCacheClearError(RuntimeException exception, Cache cache) {}
865
}
866
867
/**
868
* CacheErrorHandler implementation that logs cache provider exceptions rather than rethrowing them,
869
* allowing the underlying method to be invoked in case of cache errors.
870
*/
871
public class LoggingCacheErrorHandler implements CacheErrorHandler {
872
private final Log logger = LogFactory.getLog(getClass());
873
874
/**
875
* Log the cache get error at WARN level.
876
* @param exception the exception thrown by the cache provider
877
* @param cache the cache
878
* @param key the key used to get the item
879
*/
880
@Override
881
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {}
882
883
/**
884
* Log the cache put error at WARN level.
885
* @param exception the exception thrown by the cache provider
886
* @param cache the cache
887
* @param key the key used to update the item
888
* @param value the value used to update the item
889
*/
890
@Override
891
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {}
892
893
/**
894
* Log the cache evict error at WARN level.
895
* @param exception the exception thrown by the cache provider
896
* @param cache the cache
897
* @param key the key used to clear the item
898
*/
899
@Override
900
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {}
901
902
/**
903
* Log the cache clear error at WARN level.
904
* @param exception the exception thrown by the cache provider
905
* @param cache the cache to clear
906
*/
907
@Override
908
public void handleCacheClearError(RuntimeException exception, Cache cache) {}
909
}
910
```
911
912
### Usage Examples
913
914
**Basic Caching Usage:**
915
916
```java
917
import org.springframework.cache.annotation.*;
918
import org.springframework.stereotype.Service;
919
920
@Service
921
@CacheConfig(cacheNames = "users")
922
public class UserService {
923
924
// Cache the result of this method
925
@Cacheable
926
public User findUser(Long id) {
927
System.out.println("Loading user from database: " + id);
928
// Expensive database operation
929
return userRepository.findById(id);
930
}
931
932
// Cache with specific key
933
@Cacheable(key = "#email")
934
public User findUserByEmail(String email) {
935
System.out.println("Loading user by email: " + email);
936
return userRepository.findByEmail(email);
937
}
938
939
// Cache with condition
940
@Cacheable(condition = "#id > 10")
941
public User findUserConditional(Long id) {
942
// Only cache if id > 10
943
return userRepository.findById(id);
944
}
945
946
// Cache with unless condition
947
@Cacheable(unless = "#result.isTemporary")
948
public User findUserUnless(Long id) {
949
// Don't cache if result is temporary
950
return userRepository.findById(id);
951
}
952
953
// Update cache entry
954
@CachePut(key = "#user.id")
955
public User updateUser(User user) {
956
System.out.println("Updating user: " + user.getId());
957
User updated = userRepository.save(user);
958
return updated; // This will update the cache
959
}
960
961
// Evict cache entry
962
@CacheEvict(key = "#id")
963
public void deleteUser(Long id) {
964
System.out.println("Deleting user: " + id);
965
userRepository.deleteById(id);
966
}
967
968
// Evict all cache entries
969
@CacheEvict(allEntries = true)
970
public void deleteAllUsers() {
971
System.out.println("Deleting all users");
972
userRepository.deleteAll();
973
}
974
}
975
```
976
977
**Advanced Caching Patterns:**
978
979
```java
980
@Service
981
public class ProductService {
982
983
// Multiple cache operations
984
@Caching(
985
cacheable = {
986
@Cacheable(cacheNames = "products", key = "#id"),
987
@Cacheable(cacheNames = "productsByCategory", key = "#result.category.id", condition = "#result != null")
988
},
989
put = {
990
@CachePut(cacheNames = "recentProducts", key = "'recent'")
991
}
992
)
993
public Product findProduct(Long id) {
994
return productRepository.findById(id);
995
}
996
997
// Complex key generation with SpEL
998
@Cacheable(
999
cacheNames = "productSearch",
1000
key = "T(String).format('%s_%s_%d_%d', #category, #brand, #minPrice, #maxPrice)"
1001
)
1002
public List<Product> searchProducts(String category, String brand, int minPrice, int maxPrice) {
1003
return productRepository.search(category, brand, minPrice, maxPrice);
1004
}
1005
1006
// Using method parameter properties in key
1007
@Cacheable(cacheNames = "userProducts", key = "#user.id + '_' + #category")
1008
public List<Product> getUserProducts(User user, String category) {
1009
return productRepository.findByUserAndCategory(user.getId(), category);
1010
}
1011
1012
// Synchronized cache access for expensive operations
1013
@Cacheable(cacheNames = "expensiveCalculations", key = "#input", sync = true)
1014
public BigDecimal performExpensiveCalculation(String input) {
1015
// This ensures only one thread calculates for the same input
1016
try {
1017
Thread.sleep(5000); // Simulate expensive operation
1018
return new BigDecimal(input.hashCode());
1019
} catch (InterruptedException e) {
1020
Thread.currentThread().interrupt();
1021
throw new RuntimeException(e);
1022
}
1023
}
1024
1025
// Conditional eviction
1026
@CacheEvict(
1027
cacheNames = "products",
1028
key = "#product.id",
1029
condition = "#product.isPublished"
1030
)
1031
public Product unpublishProduct(Product product) {
1032
product.setPublished(false);
1033
return productRepository.save(product);
1034
}
1035
1036
// Evict before method invocation
1037
@CacheEvict(
1038
cacheNames = "productStatistics",
1039
allEntries = true,
1040
beforeInvocation = true
1041
)
1042
public void updateProductPrices() {
1043
// Clear statistics cache before updating prices
1044
// This ensures cache is cleared even if method fails
1045
productRepository.updateAllPrices();
1046
}
1047
}
1048
```
1049
1050
**Custom Cache Configuration:**
1051
1052
```java
1053
@Configuration
1054
@EnableCaching
1055
public class CacheConfig implements CachingConfigurer {
1056
1057
@Bean
1058
@Override
1059
public CacheManager cacheManager() {
1060
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
1061
1062
// Pre-configure cache names
1063
cacheManager.setCacheNames(Arrays.asList(
1064
"users", "products", "categories", "orders"
1065
));
1066
1067
cacheManager.setAllowNullValues(false);
1068
return cacheManager;
1069
}
1070
1071
@Bean
1072
@Override
1073
public KeyGenerator keyGenerator() {
1074
return new CustomKeyGenerator();
1075
}
1076
1077
@Bean
1078
@Override
1079
public CacheErrorHandler errorHandler() {
1080
return new LoggingCacheErrorHandler();
1081
}
1082
1083
// Additional cache manager for specific use cases
1084
@Bean("simpleCacheManager")
1085
public CacheManager simpleCacheManager() {
1086
SimpleCacheManager manager = new SimpleCacheManager();
1087
1088
// Create custom caches
1089
List<Cache> caches = new ArrayList<>();
1090
caches.add(new ConcurrentMapCache("temp"));
1091
caches.add(new ConcurrentMapCache("session"));
1092
1093
manager.setCaches(caches);
1094
return manager;
1095
}
1096
}
1097
1098
// Custom key generator
1099
public class CustomKeyGenerator implements KeyGenerator {
1100
1101
@Override
1102
public Object generate(Object target, Method method, Object... params) {
1103
StringBuilder keyBuilder = new StringBuilder();
1104
keyBuilder.append(target.getClass().getSimpleName()).append(".");
1105
keyBuilder.append(method.getName()).append("(");
1106
1107
for (int i = 0; i < params.length; i++) {
1108
if (i > 0) {
1109
keyBuilder.append(",");
1110
}
1111
1112
Object param = params[i];
1113
if (param == null) {
1114
keyBuilder.append("null");
1115
} else if (param instanceof String || param instanceof Number) {
1116
keyBuilder.append(param);
1117
} else {
1118
keyBuilder.append(param.hashCode());
1119
}
1120
}
1121
1122
keyBuilder.append(")");
1123
return keyBuilder.toString();
1124
}
1125
}
1126
```
1127
1128
**Multiple Cache Managers:**
1129
1130
```java
1131
@Configuration
1132
@EnableCaching
1133
public class MultiCacheConfig {
1134
1135
@Bean
1136
@Primary
1137
public CacheManager primaryCacheManager() {
1138
ConcurrentMapCacheManager manager = new ConcurrentMapCacheManager();
1139
manager.setCacheNames(Arrays.asList("users", "products"));
1140
return manager;
1141
}
1142
1143
@Bean("secondaryCacheManager")
1144
public CacheManager secondaryCacheManager() {
1145
ConcurrentMapCacheManager manager = new ConcurrentMapCacheManager();
1146
manager.setCacheNames(Arrays.asList("sessions", "temp"));
1147
manager.setAllowNullValues(true);
1148
return manager;
1149
}
1150
1151
@Bean
1152
public CacheManager compositeCacheManager() {
1153
CompositeCacheManager compositeManager = new CompositeCacheManager();
1154
compositeManager.setCacheManagers(
1155
primaryCacheManager(),
1156
secondaryCacheManager()
1157
);
1158
compositeManager.setFallbackToNoOpCache(true);
1159
return compositeManager;
1160
}
1161
}
1162
1163
@Service
1164
public class MultiCacheService {
1165
1166
// Use primary cache manager
1167
@Cacheable(cacheNames = "users")
1168
public User getUser(Long id) {
1169
return userRepository.findById(id);
1170
}
1171
1172
// Use specific cache manager
1173
@Cacheable(cacheNames = "sessions", cacheManager = "secondaryCacheManager")
1174
public Session getSession(String sessionId) {
1175
return sessionRepository.findById(sessionId);
1176
}
1177
}
1178
```
1179
1180
**Programmatic Cache Usage:**
1181
1182
```java
1183
@Service
1184
public class ProgrammaticCacheService {
1185
1186
@Autowired
1187
private CacheManager cacheManager;
1188
1189
public User getUserWithFallback(Long id) {
1190
Cache userCache = cacheManager.getCache("users");
1191
1192
// Try to get from cache first
1193
Cache.ValueWrapper wrapper = userCache.get(id);
1194
if (wrapper != null) {
1195
return (User) wrapper.get();
1196
}
1197
1198
// Load from database if not in cache
1199
User user = userRepository.findById(id);
1200
1201
// Put in cache for future requests
1202
if (user != null) {
1203
userCache.put(id, user);
1204
}
1205
1206
return user;
1207
}
1208
1209
public void updateUserCache(User user) {
1210
Cache userCache = cacheManager.getCache("users");
1211
1212
// Update cache entry
1213
userCache.put(user.getId(), user);
1214
1215
// Also update related caches
1216
Cache usersByEmailCache = cacheManager.getCache("usersByEmail");
1217
if (usersByEmailCache != null) {
1218
usersByEmailCache.put(user.getEmail(), user);
1219
}
1220
}
1221
1222
public void invalidateUserCaches(Long userId) {
1223
// Evict from multiple caches
1224
cacheManager.getCache("users").evict(userId);
1225
1226
// Clear related caches if needed
1227
Cache statisticsCache = cacheManager.getCache("userStatistics");
1228
if (statisticsCache != null) {
1229
statisticsCache.clear();
1230
}
1231
}
1232
1233
public void warmUpCache() {
1234
Cache userCache = cacheManager.getCache("users");
1235
1236
// Pre-load frequently accessed users
1237
List<User> frequentUsers = userRepository.findFrequentlyAccessed();
1238
for (User user : frequentUsers) {
1239
userCache.put(user.getId(), user);
1240
}
1241
}
1242
}
1243
```