Backend Development 5 min read

How to Extend Spring Cache for Multi‑Tenant Isolation and TTL

This article explains Spring's annotation‑based caching, analyzes why the default cache key collides in multi‑tenant scenarios, shows how to embed tenant identifiers into keys, and provides a custom CacheManager implementation that parses TTL from the cache name and applies it to Redis entries.

Java Architecture Diary
Java Architecture Diary
Java Architecture Diary
How to Extend Spring Cache for Multi‑Tenant Isolation and TTL

Spring Cache Concept

Spring supports annotation‑based caching, which is an abstraction rather than a concrete cache implementation (e.g., EHCache or OSCache). By adding a few Spring annotations to existing code, method return values can be cached automatically.

Multi‑tenant Cache Issue

By default, when tenant A calls a method with parameter

K1

, Spring Cache stores the result under a Redis key derived from

K1

. If tenant B later calls the same method with the same

K1

, it will read the same cached value, causing data leakage.

In a multi‑tenant system, the same logical parameter from different tenants should not share the same cache entry.

The default Spring Cache key generation cannot satisfy tenant isolation requirements.

Adding Tenant Identifier to Cache Key

For tenant A, the cache key is built as

cacheName + parameter + tenantId

.

This ensures that even if the request parameters are identical, the generated Redis keys differ per tenant, preventing dirty reads.

Custom CacheManager Implementation

<code>public class PigxClientDetailsService extends JdbcClientDetailsService {
    @Cacheable(value = SecurityConstants.CLIENT_DETAILS_KEY, key = "#clientId")
    public ClientDetails loadClientByClientId(String clientId) {
        return super.loadClientByClientId(clientId);
    }
}
</code>
<code>@Slf4j
public class RedisAutoCacheManager extends RedisCacheManager {
    /**
     * Retrieve tenant ID from context and rewrite @Cacheable value.
     */
    @Override
    public Cache getCache(String name) {
        return super.getCache(TenantContextHolder.getTenantId() + StrUtil.COLON + name);
    }
}
</code>
<code>public class RedisAutoCacheManager extends RedisCacheManager {
    private static final String SPLIT_FLAG = "#";
    private static final int CACHE_LENGTH = 2;

    @Override
    protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
        if (StrUtil.isBlank(name) || !name.contains(SPLIT_FLAG)) {
            return super.createRedisCache(name, cacheConfig);
        }
        String[] cacheArray = name.split(SPLIT_FLAG);
        if (cacheArray.length < CACHE_LENGTH) {
            return super.createRedisCache(name, cacheConfig);
        }
        if (cacheConfig != null) {
            long cacheAge = Long.parseLong(cacheArray[1]);
            cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(cacheAge));
        }
        return super.createRedisCache(name, cacheConfig);
    }

    @Override
    public void put(Object key, @Nullable Object value) {
        Object cacheValue = preProcessCacheValue(value);
        if (!isAllowNullValues() && cacheValue == null) {
            throw new IllegalArgumentException(String.format(
                "Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
                name));
        }
        cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
    }
}
</code>

Summary

Extending Spring Cache allows transparent handling of multi‑tenant isolation and custom expiration.

The custom CacheManager serves as the extension point for Spring Cache APIs.

The example code is taken from a personal project built on Spring Cloud, OAuth2.0, and a Vue front‑end.

JavaRedisSpring Bootmulti-tenantSpring CacheCacheManager
Java Architecture Diary
Written by

Java Architecture Diary

Committed to sharing original, high‑quality technical articles; no fluff or promotional content.

0 followers
Reader feedback

How this landed with the community

login 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.