PrepZone Logo
PrepZone

Spring Cache + Redis

@Cacheable, cache eviction and Redis as a distributed cache backend.

Why this matters

  • Catalog lookups by ISBN dominate BookStore traffic — caching bestseller lists and book details cuts database load by orders of magnitude.
  • A local in-memory cache per JVM goes stale when inventory changes on another node; Redis gives every instance the same view.
  • @Cacheable keeps caching logic out of business code — the service method looks like a plain database read until you inspect the annotations.

Cache-aside with ElastiCache

1. GET2. miss3. SET+TTLCOMPUTE
EKS API podstreamhub-api
DATABASE
RDS Postgressource of truth
DATABASE
ElastiCachesub-ms reads
App checks Redis first; on miss loads RDS and populates cache with TTL.

Cache abstraction pieces

  • @EnableCaching — activates Spring's cache infrastructure and AOP proxies.
  • @Cacheable — stores the method return value; subsequent calls with the same key skip execution.
  • @CacheEvict — removes entries when data changes.
  • @CachePut — always runs the method and updates the cache with the fresh result.
  • RedisCacheManager — maps cache names to Redis keys with optional TTL per cache.

Enable caching with Redis

Java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Java
spring:
  data:
    redis:
      host: 127.0.0.1
      port: 6379
  cache:
    type: redis
    redis:
      time-to-live: 600000  # 10 minutes default
Java
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration defaults = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10))
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair
                                .fromSerializer(new GenericJackson2JsonRedisSerializer()));

        Map<String, RedisCacheConfiguration> perCache = Map.of(
                "books", defaults.entryTtl(Duration.ofHours(1)),
                "bestsellers", defaults.entryTtl(Duration.ofMinutes(5))
        );

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(defaults)
                .withInitialCacheConfigurations(perCache)
                .build();
    }
}

Cache reads, evict on writes

Java
@Service
public class BookService {

    private final BookRepository bookRepository;

    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    @Cacheable(value = "books", key = "#isbn")
    public BookDetail getByIsbn(String isbn) {
        return bookRepository.findByIsbn(isbn)
                .map(BookDetail::from)
                .orElseThrow(() -> new BookNotFoundException(isbn));
    }

    @Cacheable(value = "bestsellers", key = "'top-' + #limit")
    public List<BookSummary> getBestsellers(int limit) {
        return bookRepository.findTopSellers(limit)
                .stream()
                .map(BookSummary::from)
                .toList();
    }

    @Caching(evict = {
            @CacheEvict(value = "books", key = "#book.isbn"),
            @CacheEvict(value = "bestsellers", allEntries = true)
    })
    public BookDetail updateBook(Book book) {
        Book saved = bookRepository.save(book);
        return BookDetail.from(saved);
    }
}

Programmatic cache access

Java
@Service
public class CatalogWarmupService {

    private final CacheManager cacheManager;
    private final BookService bookService;

    public void preloadFeaturedTitles(List<String> isbns) {
        Cache books = cacheManager.getCache("books");
        isbns.forEach(isbn -> {
            BookDetail detail = bookService.getByIsbn(isbn);
            books.put(isbn, detail);
        });
    }
}

Quick recall

Everything you need if you only revisit this box.

  • @EnableCaching + spring-boot-starter-data-redis + spring-boot-starter-cache is the standard stack.
  • @Cacheable("books") keys on method parameters by default; customise with key = "#isbn".
  • @CacheEvict on update/delete methods prevents stale reads after writes.
  • Configure per-cache TTL in RedisCacheManager — bestsellers need shorter TTL than static book metadata.
  • Redis is shared across instances; pair caching with eviction on every write path that changes cached data.

Test yourself

Answer these before moving on — recall is what makes it stick.