**Spring Cache Skill**: Production-grade caching with Redis and Spring Cache abstraction for Java 21 + Spring Boot 3.x. Covers @Cacheable, @CachePut, @CacheEvict, multi-level caching, cache-aside pattern, TTL configuration, cache warming, conditional caching, distributed cache with Lettuce, cache serialization, key generation, monitoring with Micrometer, and cache stampede prevention. MANDATORY TRIGGERS: @Cacheable, @CachePut, @CacheEvict, @Caching, @EnableCaching, CacheManager, RedisCacheManager, CacheConfiguration, RedisTemplate, Spring Cache, Redis, Lettuce, cache, TTL, cache eviction, cache hit, cache miss, cache invalidation, cache stampede, dogpile, multi-level cache, L1 cache, L2 cache, Caffeine, local cache, distributed cache, cache-aside, read-through, write-through, cache warming, preload, cache key, KeyGenerator, cache serialization, Jackson2JsonRedisSerializer, cache monitoring, cache metrics.
You are implementing caching for the Java 21 / Spring Boot 3.3+ banking platform using:
RedisCacheManager@Cacheable, @CachePut, @CacheEvict)package com.banking.platform.config;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.Map;
@Configuration
@EnableCaching
public class CacheConfig {
/** Default TTL — override per cache in cacheConfigurations() */
private static final Duration DEFAULT_TTL = Duration.ofMinutes(10);
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
var defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(DEFAULT_TTL)
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer()))
.disableCachingNullValues() // Never cache null — avoids null-masking real errors
.prefixCacheNameWith("banking:"); // Key prefix: banking:accounts::<key>
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(defaultConfig)
.withInitialCacheConfigurations(cacheConfigurations(defaultConfig))
.transactionAware() // Cache writes only committed after TX commit
.build();
}
/**
* Per-cache TTL overrides — tune based on data volatility.
* Format: cacheConfigurations.put("cacheName", defaultConfig.entryTtl(Duration))
*/
private Map<String, RedisCacheConfiguration> cacheConfigurations(RedisCacheConfiguration base) {
return Map.of(
CacheNames.ACCOUNTS, base.entryTtl(Duration.ofMinutes(15)), // Account details
CacheNames.TRANSACTIONS, base.entryTtl(Duration.ofMinutes(5)), // Recent transactions
CacheNames.EXCHANGE_RATES, base.entryTtl(Duration.ofHours(1)), // Forex rates
CacheNames.USER_PROFILES, base.entryTtl(Duration.ofMinutes(30)), // User preferences
CacheNames.PRODUCT_CONFIG, base.entryTtl(Duration.ofHours(6)) // Static config
);
}
/** Jackson serializer with type information — required for correct deserialization */
private Jackson2JsonRedisSerializer<Object> jsonSerializer() {
var mapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY // Stores class name in JSON — required for polymorphism
);
return new Jackson2JsonRedisSerializer<>(mapper, Object.class);
}
}
// Cache name constants — single source of truth
public final class CacheNames {
public static final String ACCOUNTS = "accounts";
public static final String TRANSACTIONS = "transactions";
public static final String EXCHANGE_RATES = "exchange-rates";
public static final String USER_PROFILES = "user-profiles";
public static final String PRODUCT_CONFIG = "product-config";
private CacheNames() {}
}