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> T get(Object key, @Nullable Class<T> type);
@Nullable
<T> T get(Object key, @Nullable Callable<T> 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> T get(Object key, @Nullable Class<T> 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<Object, Object> 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<Object, Object> store, boolean allowNullValues, @Nullable SerializationDelegate serialization) { ... }
public final String getName() { return this.name; }
public final ConcurrentMap<Object, Object> getNativeCache() { return this.store; }
@Nullable
protected Object lookup(Object key) { return this.store.get(key); }
public <T> T get(Object key, Callable<T> 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) { ... }
} ConcurrentMapCacheis 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<String> 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<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<>(Employee.class);
template.setDefaultSerializer(ser);
return template;
}
}Custom CacheManager for Redis
@Bean
public RedisCacheManager employeeCacheManager(RedisTemplate<Object,Object> 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
