Spring Boot Cache: JCache Specification, Cache Abstraction, Annotations, and Redis Integration
This article explains Spring Boot caching by introducing the JSR‑107 JCache specification, detailing core cache interfaces, showing the Spring Cache abstraction and its implementations, demonstrating cache annotations such as @Cacheable, @CachePut and @CacheEvict, and finally covering Redis setup and custom CacheManager usage.
Spring Boot caching relies on the JSR‑107 (JCache) specification, which defines standard cache interfaces such as CachingProvider , CacheManager , Cache , Entry and Expiry . The article first describes these interfaces and the role of JCache in Java.
JCache
JSR‑107 (JCache) provides a unified API for cache operations like get, put, evict, and expiration handling across different cache providers.
1. JCache Core Interfaces
A CachingProvider can create and manage multiple CacheManager instances.
Each CacheManager manages uniquely named Cache objects.
Cache behaves like a map storing key‑value Entry objects.
Expiry defines the lifetime of a cache entry.
2. Spring Cache Abstraction
Since Spring 3.1, Spring provides a cache abstraction with org.springframework.cache.Cache and org.springframework.cache.CacheManager . The Cache interface defines operations such as get , put , evict , and clear . Implementations include RedisCache , EhCacheCache , and ConcurrentMapCache .
Cache Interface
public interface Cache {
String getName();
Object getNativeCache();
@Nullable
Cache.ValueWrapper get(Object key);
@Nullable
T get(Object key, @Nullable Class
type);
@Nullable
T get(Object key, @Nullable Callable
valueLoader);
void put(Object key, @Nullable Object value);
@Nullable
default ValueWrapper putIfAbsent(Object key, @Nullable Object value) { ... }
void evict(Object key);
default boolean evictIfPresent(Object key) { ... }
void clear();
default boolean invalidate() { ... }
class ValueRetrievalException extends RuntimeException { ... }
@FunctionalInterface
interface ValueWrapper { @Nullable Object get(); }
}The Cache interface abstracts cache operations, while the ValueWrapper provides a simple wrapper for cached values.
AbstractValueAdaptingCache
public abstract class AbstractValueAdaptingCache implements Cache {
private final boolean allowNullValues;
protected AbstractValueAdaptingCache(boolean allowNullValues) { this.allowNullValues = allowNullValues; }
public final boolean isAllowNullValues() { return this.allowNullValues; }
@Nullable
public ValueWrapper get(Object key) { return toValueWrapper(lookup(key)); }
@Nullable
public
T get(Object key, @Nullable Class
type) { ... }
@Nullable
protected abstract Object lookup(Object key);
@Nullable
protected Object fromStoreValue(@Nullable Object storeValue) { ... }
protected Object toStoreValue(@Nullable Object userValue) { ... }
@Nullable
protected ValueWrapper toValueWrapper(@Nullable Object storeValue) { ... }
}This abstract class handles null‑value semantics for caches, allowing or rejecting null entries based on the allowNullValues flag.
ConcurrentMapCache
public class ConcurrentMapCache extends AbstractValueAdaptingCache {
private final String name;
private final ConcurrentMap
store;
private final SerializationDelegate serialization;
public ConcurrentMapCache(String name) { this(name, new ConcurrentHashMap(256), true); }
public ConcurrentMapCache(String name, boolean allowNullValues) { this(name, new ConcurrentHashMap(256), allowNullValues); }
protected ConcurrentMapCache(String name, ConcurrentMap
store, boolean allowNullValues, @Nullable SerializationDelegate serialization) { ... }
public final String getName() { return this.name; }
public final ConcurrentMap
getNativeCache() { return this.store; }
@Nullable
protected Object lookup(Object key) { return this.store.get(key); }
public
T get(Object key, Callable
valueLoader) { ... }
public void put(Object key, @Nullable Object value) { this.store.put(key, toStoreValue(value)); }
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) { ... }
public void evict(Object key) { this.store.remove(key); }
public boolean evictIfPresent(Object key) { return this.store.remove(key) != null; }
public void clear() { this.store.clear(); }
public boolean invalidate() { boolean notEmpty = !this.store.isEmpty(); this.store.clear(); return notEmpty; }
protected Object toStoreValue(@Nullable Object userValue) { ... }
protected Object fromStoreValue(@Nullable Object storeValue) { ... }
}ConcurrentMapCache is Spring's default in‑memory cache implementation, optionally supporting value copying via a SerializationDelegate .
CacheManager Interface
public interface CacheManager {
@Nullable Cache getCache(String name);
Collection
getCacheNames();
}Implementations such as ConcurrentMapCacheManager , SimpleCacheManager , and RedisCacheManager manage groups of caches identified by names.
3. Spring Cache Annotations
The article explains the use of @EnableCaching to activate annotation‑driven caching and details the three main method‑level annotations:
@Cacheable – caches the result of a method call.
@CachePut – always executes the method and updates the cache.
@CacheEvict – removes entries from the cache.
It also covers advanced annotations @Caching (combining multiple cache operations) and @CacheConfig (class‑level default cache settings).
Example of @Cacheable
@Cacheable(cacheNames = {"emp"}, key = "#id", condition = "#id > 0", unless = "#result == null")
public Employee getEmp(Integer id) {
Employee emp = employeeMapper.getEmpById(id);
return emp;
}Example of @CachePut
@CachePut(value = "emp", key = "#result.id")
public Employee updateEmp(Employee employee) {
employeeMapper.updateEmp(employee);
return employee;
}Example of @CacheEvict
@CacheEvict(value = "emp", key = "#id", allEntries = true)
public void deleteEmp(Integer id) {
employeeMapper.deleteEmpById(id);
}4. Redis Integration
The default Spring Boot cache uses ConcurrentMapCache . For production, Redis is commonly used. The article shows how to start a Redis container with Docker, connect using Redis Desktop Manager, and use StringRedisTemplate and RedisTemplate for various data structures (String, List, Set, Hash, ZSet).
Redis Test Code
public class RedisTest {
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RedisTemplate redisTemplate;
public void test01() {
stringRedisTemplate.opsForValue().append("msg", "hello");
String s = stringRedisTemplate.opsForValue().get("msg");
stringRedisTemplate.opsForList().leftPush("mylist", "1");
}
public void test02() {
Employee emp = new Employee();
redisTemplate.opsForValue().set("emp-01", emp);
}
}To store objects as JSON, the article suggests customizing RedisTemplate with a Jackson2JsonRedisSerializer or converting objects to JSON manually.
Custom RedisTemplate Example
public class MyRedisConfig {
@Bean
public RedisTemplate
empRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate
template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer
ser = new Jackson2JsonRedisSerializer<>(Employee.class);
template.setDefaultSerializer(ser);
return template;
}
}Custom CacheManager for Redis
@Bean
public RedisCacheManager employeeCacheManager(RedisTemplate
employeeRedisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(employeeRedisTemplate);
cacheManager.setUsePrefix(true);
return cacheManager;
}This custom RedisCacheManager allows prefixing cache names and using a JSON‑based RedisTemplate for serialization.
Overall, the article provides a comprehensive guide to Spring Boot caching, from the JCache specification to practical Redis integration, and demonstrates how to configure and use cache annotations effectively.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.