skip to content

How do you give different caches different TTLs and serializers within a single RedisCacheManager?

level: middleimportance: should knowfreq 40%

answer

  1. builder(cf).cacheDefaults(...).withInitialCacheConfigurations(map)
  2. derive per-cache from immutable defaults
  3. disableCreateOnMissingCache = fail fast on typos
  4. RedisCacheManagerBuilderCustomizer for Boot
  5. each cache keeps its cacheName:: prefix

basics

~10 s

Build the manager with RedisCacheManager.builder(cf), set a default via cacheDefaults(cfg), then override specific caches with withInitialCacheConfigurations(Map<name, RedisCacheConfiguration>) — each entry a config with its own TTL/serializer.

solid answer

~30 s

RedisCacheManagerBuilder lets one manager serve many caches with distinct behavior. cacheDefaults(cfg) sets the RedisCacheConfiguration used by any cache without an explicit override. Per-cache overrides come from withInitialCacheConfigurations(Map<String,RedisCacheConfiguration>) or repeated withCacheConfiguration(name, cfg). Since RedisCacheConfiguration is immutable, derive each variant from the default: defaults.entryTtl(Duration.ofSeconds(30)) for 'sessions', defaults.entryTtl(Duration.ofHours(24)) for 'reference'. You can also add disableCreateOnMissingCache() so only pre-declared cache names are allowed (typos fail fast), and in Boot use a RedisCacheManagerBuilderCustomizer to tweak the auto-configured manager without replacing the bean. Each named cache still writes keys under its own cacheName:: prefix, so they coexist safely in one Redis keyspace.

code

java · 13 lines
java
@Bean
RedisCacheManagerBuilderCustomizer perCacheTtl() {
    return builder -> {
        RedisCacheConfiguration base = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        builder
            .cacheDefaults(base.entryTtl(Duration.ofMinutes(10)))
            .withCacheConfiguration("sessions",  base.entryTtl(Duration.ofSeconds(30)))
            .withCacheConfiguration("reference", base.entryTtl(Duration.ofHours(24)))
            .disableCreateOnMissingCache();
    };
}

go deeper

for a junior

Know that TTL can differ per cache and it's set on the manager builder.

for a middle

Use cacheDefaults + withInitialCacheConfigurations/withCacheConfiguration and derive from an immutable default.

for a senior

Add fail-fast (disableCreateOnMissingCache), transactionAware, and Boot's builder customizer; reason about prefix coexistence.

for a principal

Set org conventions for per-domain TTL/serialization and avoid the ZERO-TTL default trap across many caches and services.

**Why one manager, many configs.** Different data has different freshness needs: session data might want a 30-second TTL, reference/lookup data a 24-hour TTL, and a large blob cache a JSON serializer while small values stay JDK-serialized. A single `RedisCacheManager` supports all of this through `RedisCacheManagerBuilder`. **The builder API.** - `RedisCacheManager.builder(RedisConnectionFactory cf)` — start (there is also `builder(RedisCacheWriter)` for advanced control over how writes/locks happen). - `.cacheDefaults(RedisCacheConfiguration def)` — the fallback config for any cache name not explicitly overridden. - `.withInitialCacheConfigurations(Map<String, RedisCacheConfiguration>)` — bulk per-cache overrides. - `.withCacheConfiguration(String name, RedisCacheConfiguration cfg)` — one override at a time (chainable). - `.disableCreateOnMissingCache()` — reject `@Cacheable("typo")` for names you didn't pre-register, instead of silently creating them with defaults. - `.initialCacheNames(Set<String>)` — eagerly create these caches at startup. - `.transactionAware()` — bind cache writes to the surrounding transaction so evicts/puts only apply on commit. **Immutability drives the pattern.** Because `RedisCacheConfiguration` is immutable, you build a `defaults` once and *derive* each per-cache variant: `defaults.entryTtl(...)`, `defaults.serializeValuesWith(...)`. The default's serializers/prefix carry over unless the derived call changes them. **Boot integration.** If you like Boot's auto-configured `RedisCacheManager` but need per-cache tweaks, don't replace the bean — add a `RedisCacheManagerBuilderCustomizer` bean; Boot calls it against the builder before `build()`. Property `spring.cache.cache-names` can pre-declare names, and `spring.cache.redis.*` sets the default TTL/prefix/null behavior. **Keyspace coexistence.** Each named cache prefixes its keys with `cacheName::` by default, so `sessions::abc` and `reference::abc` never collide, and `clear()`/`@CacheEvict(allEntries=true)` on one cache only scans-and-deletes that cache's prefix. **Gotchas.** (1) A cache name used in `@Cacheable` but absent from your config map falls back to `cacheDefaults` — easy to accidentally get 'no TTL' if your default has `Duration.ZERO`. (2) `withInitialCacheConfigurations` only configures caches known at build time; caches created later still use defaults. (3) Mixing value serializers across caches means consumers reading a cache must know its serializer — keep it consistent per cache.

  • What happens if @Cacheable names a cache you never configured?
    It falls back to cacheDefaults and is created lazily — unless you called disableCreateOnMissingCache(), which makes an unknown cache name throw instead.
  • In Spring Boot, how do you tweak per-cache config without dropping the auto-configured manager?
    Register a RedisCacheManagerBuilderCustomizer bean. Boot applies it to the builder it already set up, so you keep auto-configuration and only override what you need.

saying these in an interview costs you the question

  • Thinking one RedisCacheManager can only have one global TTL/serializer
  • Mutating a shared RedisCacheConfiguration expecting per-cache divergence (it's immutable — derive new ones)
  • Assuming unknown cache names error by default (they're created with defaults unless disableCreateOnMissingCache())

context