Spring Cache
Quick Start
@SpringBootApplication
@EnableCaching
public class Application {}
@Service
public class UserService {
@Cacheable("users")
public User findById(Long id) {
return userRepository.findById(id).orElseThrow();
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=1000,expireAfterWrite=10m
Cache Annotations
@Cacheable
@Cacheable("products")
public Product findById(Long id) { }
@Cacheable(value = "products", key = "#category + '-' + #status")
public List<Product> findByCategoryAndStatus(String category, String status) { }
@Cacheable(value = "products", condition = "#id > 0")
public Product findByIdConditional(Long id) { }
@Cacheable(value = "products", unless = "#result == null")
public Product findByIdUnlessNull(Long id) { }
@Cacheable(value = "products", sync = true) // One thread populates
public Product findByIdSync(Long id) { }
@CacheEvict
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(Long id) { }
@CacheEvict(value = "products", allEntries = true)
public void clearProductCache() { }
@CacheEvict(value = "products", key = "#id", beforeInvocation = true)
public void deleteProductBeforeInvocation(Long id) { }
@CachePut
@CachePut(value = "products", key = "#product.id")
public Product saveProduct(Product product) {
return productRepository.save(product);
}
@CachePut(value = "products", key = "#result.id")
public Product createProduct(CreateProductRequest request) {
return productRepository.save(new Product(request));
}
@Caching (Multiple Operations)
@Caching(
put = {
@CachePut(value = "products", key = "#result.id"),
@CachePut(value = "productsBySku", key = "#result.sku")
},
evict = {
@CacheEvict(value = "productList", allEntries = true)
}
)
public Product createProduct(CreateProductRequest request) { }
@CacheConfig (Class-Level)
@Service
@CacheConfig(cacheNames = "products", keyGenerator = "customKeyGenerator")
public class ProductService {
@Cacheable // Uses class config
public Product findById(Long id) { }
@Cacheable(cacheNames = "inventory") // Override cache name
public Inventory getInventory(Long productId) { }
}
Full Reference: See managers.md for Caffeine, Redis, EhCache configurations.
Quick Cache Manager Setup
Caffeine (Single Instance)
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCaffeine(Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(Duration.ofMinutes(10))
.recordStats());
return manager;
}
Redis (Distributed)
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeValuesWith(SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
Full Reference: See advanced.md for Multi-Level Caching, Metrics, Synchronization.
Best Practices
| Do |
Don't |
| Use Caffeine for single-instance |
Skip TTL configuration |
| Use Redis for distributed |
Cache mutable objects |
| Configure TTL always |
Ignore cache eviction |
| Use sync=true for expensive ops |
Use high cardinality keys |
| Implement cache metrics |
Cache sensitive data unencrypted |
Production Checklist
When NOT to Use This Skill
- Distributed caching - Use
spring-data-redis
- Redis operations - Use
redis skill
- Session storage - Use Spring Session
Common Pitfalls
| Error |
Cause |
Solution |
| Cache not working |
Internal call (same bean) |
Use self-injection |
| Null pointer |
Null values cached |
Use unless = "#result == null" |
| Memory leak |
TTL not configured |
Set expireAfterWrite |
| Serialization error |
Non-serializable objects |
Implement Serializable |
Anti-Patterns
| Anti-Pattern |
Problem |
Solution |
| Caching mutable objects |
Stale data |
Cache immutable data |
| No TTL configured |
Stale cache forever |
Set expireAfterWrite |
| @Cacheable on void |
No effect |
Only cache with return |
| No cache sync |
Race conditions |
Use sync=true or locks |
Quick Troubleshooting
| Problem |
Diagnostic |
Fix |
| Cache not working |
Check @EnableCaching |
Add annotation |
| Wrong data cached |
Check cache key |
Define explicit key |
| Cache not evicted |
Check key expression |
Verify key matches |
| Self-invocation bypass |
Same class call |
Inject self |
Reference Files
| File |
Content |
| managers.md |
Caffeine, Redis, EhCache, Key Generators |
| advanced.md |
Multi-Level, Metrics, Sync, Testing |
External Documentation