Caching
IntermediateSpring's caching abstraction (@Cacheable, @CacheEvict, @CachePut) decouples cache logic from business logic. Back it with Redis for a distributed cache shared across all app instances.
Overview
Spring Cache wraps method results in a cache — on the first call the real method executes and the result is stored; on subsequent calls with the same arguments the cached value is returned without executing the method. @Cacheable is for reads, @CacheEvict is for deletes/updates, @CachePut always executes the method and updates the cache. The cache name maps to a specific store (e.g. a Redis key prefix). With Redis as the backend, the cache is shared across all pods.
@Cacheable, @CacheEvict, @CachePut
Enable caching with @EnableCaching on a @Configuration class. Cache names are logical identifiers — they map to cache regions in the CacheManager. The cache key defaults to the method arguments; use key = "#id" with SpEL for clarity.
@Configuration
@EnableCaching
public class CacheConfig { }
@Service
public class CourseService {
// Cache result — key = courseId
// courses::123 stored in Redis
@Cacheable(value = "courses", key = "#courseId")
public CourseDto getCourse(String courseId) {
return courseRepository.findById(courseId)
.map(courseMapper::toDto)
.orElseThrow(() -> new CourseNotFoundException(courseId));
// Only called on first request — cached result returned on subsequent calls
}
// Condition: only cache if course is published
@Cacheable(value = "courses", key = "#courseId",
condition = "#result.status == 'PUBLISHED'")
public CourseDto getCoursePublished(String courseId) { ... }
// Always execute method AND update cache — for write operations
@CachePut(value = "courses", key = "#result.id")
public CourseDto updateCourse(String courseId, UpdateCourseRequest req) {
Course updated = courseRepository.save(/* ... */);
return courseMapper.toDto(updated);
}
// Remove specific entry on update/delete
@CacheEvict(value = "courses", key = "#courseId")
public void deleteCourse(String courseId) {
courseRepository.deleteById(courseId);
}
// Evict ALL entries in the cache
@CacheEvict(value = "courses", allEntries = true)
@Scheduled(cron = "0 0 3 * * *") // 3 AM daily cache refresh
public void clearCourseCache() { }
}Redis CacheManager with TTL
Configure RedisCacheManager to use Redis as the cache backend. Set per-cache TTL to prevent stale data from living forever. Use JSON serialization so cached values are human-readable in Redis.
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
# application.yml
spring:
data:
redis:
host: ${REDIS_HOST:localhost}
port: 6379
password: ${REDIS_PASSWORD:}
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheConfiguration defaultCacheConfig() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // global default TTL
.serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer())); // JSON
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
Map<String, RedisCacheConfiguration> perCacheConfig = Map.of(
"courses", defaultCacheConfig().entryTtl(Duration.ofHours(1)),
"users", defaultCacheConfig().entryTtl(Duration.ofMinutes(5)),
"dsa", defaultCacheConfig().entryTtl(Duration.ofHours(24))
);
return RedisCacheManager.builder(factory)
.cacheDefaults(defaultCacheConfig())
.withInitialCacheConfigurations(perCacheConfig)
.build();
}
}
// Redis key pattern: {cacheName}::{key}
// e.g. courses::abc-123 → CourseDto JSON (expires in 1 hour)Key Points to Remember
- 1@EnableCaching activates Spring's caching proxy — without it, @Cacheable annotations are ignored.
- 2@Cacheable returns the cached value on cache hit; executes the method and caches the result on miss.
- 3@CachePut always executes the method and always updates the cache — use after write operations.
- 4@CacheEvict removes entries — use allEntries = true sparingly (removes all keys in the cache region).
- 5Use GenericJackson2JsonRedisSerializer for JSON-serialized Redis values — readable and debuggable.
- 6Always set a TTL on every cache to prevent stale data accumulating in Redis forever.
Interview Questions
Sign in to ask AriaWhat is the difference between @Cacheable and @CachePut?
How does Spring Cache know whether to return the cached value or call the method?
How would you configure different TTLs for different caches in Redis?
What are the risks of caching without a TTL?
How do you handle cache inconsistency in a multi-pod deployment?
Ask Aria about Caching
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.