Elegant Redisson‑Based CacheManager Implementation in an Open‑Source Project
The article walks through a Java backend design that replaces RedisTemplate with a Redisson‑backed CacheManager, explains the PlusSpringCacheManager class, its configuration bean, key syntax, usage examples, and compares Redisson’s cache features to traditional RedisTemplate approaches.
Background
Java backend code often uses RedisTemplate for cache operations. An alternative wraps Redisson in a custom CacheManager, enabling annotation‑driven caching without switching between Jedis, Lettuce, or RedisTemplate.
Source Project
The implementation originates from the open‑source project RuoYi‑Vue‑Plus (repository: https://gitee.com/dromara/RuoYi-Vue-Plus), which rewrites the original RuoYi‑Vue for distributed‑cluster scenarios.
Redisson‑Based CacheManager Features
The framework uses Redisson (a Netty‑based Redis client supporting >90% of Redis commands) and extends Spring‑Cache annotations to add:
Expiration time (TTL)
Maximum idle time (LRU‑based)
Maximum size per cache group
All parameters are configurable via a single annotation value.
3.1 PlusSpringCacheManager Class Definition
package com.ruoyi.framework.manager;</code>
<code>import com.ruoyi.common.utils.redis.RedisUtils;</code>
<code>import org.redisson.api.RMap;</code>
<code>import org.redisson.api.RMapCache;</code>
<code>import org.redisson.spring.cache.CacheConfig;</code>
<code>import org.redisson.spring.cache.RedissonCache;</code>
<code>import org.springframework.boot.convert.DurationStyle;</code>
<code>import org.springframework.cache.Cache;</code>
<code>import org.springframework.cache.CacheManager;</code>
<code>import org.springframework.cache.transaction.TransactionAwareCacheDecorator;</code>
<code>import org.springframework.util.StringUtils;</code>
<code>import java.util.Collection;</code>
<code>import java.util.Collections;</code>
<code>import java.util.Map;</code>
<code>import java.util.concurrent.ConcurrentHashMap;</code>
<code>import java.util.concurrent.ConcurrentMap;</code>
<code>@SuppressWarnings("unchecked")
public class PlusSpringCacheManager implements CacheManager {
private boolean dynamic = true;
private boolean allowNullValues = true;
private boolean transactionAware = true;
Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();
ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
// setters omitted for brevity
@Override
public Cache getCache(String name) {
String[] array = StringUtils.delimitedListToStringArray(name, "#");
name = array[0];
Cache cache = instanceMap.get(name);
if (cache != null) return cache;
if (!dynamic) return null;
CacheConfig config = configMap.get(name);
if (config == null) {
config = createDefaultConfig();
configMap.put(name, config);
}
if (array.length > 1) config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());
if (array.length > 2) config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());
if (array.length > 3) config.setMaxSize(Integer.parseInt(array[3]));
if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {
return createMap(name, config);
}
return createMapCache(name, config);
}
private Cache createMap(String name, CacheConfig config) {
RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
Cache cache = new RedissonCache(map, allowNullValues);
if (transactionAware) cache = new TransactionAwareCacheDecorator(cache);
Cache old = instanceMap.putIfAbsent(name, cache);
return old != null ? old : cache;
}
private Cache createMapCache(String name, CacheConfig config) {
RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
Cache cache = new RedissonCache(map, config, allowNullValues);
if (transactionAware) cache = new TransactionAwareCacheDecorator(cache);
Cache old = instanceMap.putIfAbsent(name, cache);
if (old != null) return old;
map.setMaxSize(config.getMaxSize());
return cache;
}
@Override
public Collection<String> getCacheNames() {
return Collections.unmodifiableSet(configMap.keySet());
}
}3.2 Defining the Configuration Bean
@Bean
public CacheManager cacheManager() {
return new PlusSpringCacheManager();
}3.3 Using the CacheManager in a Project
Cache name format: cacheNames#ttl#maxIdleTime#maxSize ttl : expiration time (0 = never expires)
maxIdleTime : LRU‑based idle eviction (0 = disabled)
maxSize : maximum entry count (0 = unlimited)
public class CacheNames {
String DEMO_CACHE = "demo:cache#60s#10m#20";
String SYS_USER_NAME = "sys_user_name#30d";
String SYS_DEPT = "sys_dept#30d";
String SYS_OSS = "sys_oss#30d";
}Applying Spring Cache annotations:
@Cacheable(cacheNames = CacheNames.SYS_DEPT, key = "#deptId")
public SysDept selectDeptById(Long deptId) { … }
@CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#dept.deptId")
public int updateDept(SysDept dept) { … }3.4 Effect of the Implementation
Cached entries are stored in a Redis hash structure; multiple logical values share the same Redis key but occupy different hash fields. Background threads maintain expiration, idle eviction, and size‑based eviction according to the configured TTL, idle time, and max size.
3.5 Comparison Between Redisson and RedisTemplate (ChatGPT Summary)
API richness : Redisson provides distributed locks, collections, objects, and cache APIs, while RedisTemplate focuses on basic Redis data‑structure operations.
Usage style : Redisson supports annotation‑driven caching; RedisTemplate requires explicit code for cache reads/writes.
Distributed support : Redisson includes built‑in distributed primitives; RedisTemplate leaves distributed coordination to user‑implemented solutions.
Ease of use : Redisson’s API and annotations are concise; RedisTemplate needs more boilerplate.
Performance : Redisson optimizes network communication and can use asynchronous operations, generally yielding better performance than RedisTemplate, though exact gains depend on workload.
3.6 Example of Using RedissonSpringCacheManager
@Configuration
public class CacheConfig {
@Bean
public RedissonSpringCacheManager cacheManager(RedissonClient redissonClient) {
return new RedissonSpringCacheManager(redissonClient, "classpath:/cache-config.yaml");
}
}Methods annotated with @Cacheable, @CachePut, or @CacheEvict will be handled by the Redisson‑backed manager, with cache expiration and eviction governed by the YAML configuration.
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.
Ubiquitous Tech
A ubiquitous public account for pirate enthusiasts, regularly sharing curated experiences, tech learning, and growth insights. Currently publishing articles on AI RAG customer service, AI MCP technology, and open-source design. Personal free Knowledge Planet: Awakening New World Programmer.
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.
