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.

Ubiquitous Tech
Ubiquitous Tech
Ubiquitous Tech
Elegant Redisson‑Based CacheManager Implementation in an Open‑Source Project

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaredisRedissonSpring CacheCacheManagerRuoYi-Vue-Plus
Ubiquitous Tech
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.