Learnitweb

Advanced Spring Boot Caching: @Cacheable, @CachePut, @CacheEvict, and Scheduled Updates

Caching is essential for improving application performance and reducing load on external resources. Spring Boot provides several annotations to manage caching in a flexible manner.


1. Understanding the Core Annotations

1.1 @Cacheable

  • Purpose: Avoids method execution if a cached result is available.
  • How it works:
    • Checks if the key exists in cache.
    • If present, returns cached value.
    • If absent, executes the method and caches the result.
  • Use case: Expensive calculations, external API calls, repeated GET requests.
@Cacheable(value = "weather", key = "#zip")
public int getWeather(String zip) {
    // Method execution occurs only if cache doesn't exist
    return fetchWeatherFromExternalService(zip);
}

Key points:

  • Skips execution whenever possible.
  • Caches results for future requests.
  • Ideal for GET operations where the data is mostly read-only.

1.2 @CacheEvict

  • Purpose: Removes cache entries.
  • When to use: During updates, deletes, or when you want to refresh cached data.
  • Important property: beforeInvocation
    • If true, cache is cleared before method execution.
    • Default: false (clears cache after successful method execution).
@CacheEvict(value = "weather", key = "#zip", beforeInvocation = true)
public void clearWeatherCache(String zip) {
    // Cache will be evicted before executing this method
}

Notes:

  • Ensures stale data is removed.
  • Critical for POST, PUT, DELETE operations where data changes.

1.3 @CachePut

  • Purpose: Updates cache every time method executes.
  • Difference from @Cacheable:
    • Always executes the method.
    • Stores the result in cache, overwriting existing entry.
@CachePut(value = "weather", key = "#zip")
public int updateWeather(String zip) {
    return fetchWeatherFromExternalService(zip);
}

Use case:

  • Hybrid approach where you want both execution and cache update.
  • Useful for pre-fetching data periodically.

2. Practical Example: Weather Service with Scheduled Cache Updates

2.1 Use Case

  • Application provides weather info to users.
  • External API calls are slow or costly.
  • Goal: Provide fast, reliable responses while keeping data reasonably fresh.

2.2 Service Layer

@Service
public class WeatherService {

    private final ExternalWeatherService externalService;

    public WeatherService(ExternalWeatherService externalService) {
        this.externalService = externalService;
    }

    @Cacheable(value = "weather", key = "#zip")
    public int getWeather(String zip) {
        // Will be skipped if cached
        return 0; // Placeholder
    }

    @Scheduled(fixedRate = 10000) // Every 10 seconds
    @CachePut(value = "weather", key = "#zip")
    public int updateWeather(String zip) {
        // Periodically refreshes cache
        return externalService.fetchWeather(zip);
    }
}

2.3 External Service Simulation

@Service
public class ExternalWeatherService {

    public int fetchWeather(String zip) {
        // Simulate external API call
        return new Random().nextInt(100); // Random temperature
    }
}

2.4 Controller Layer

@RestController
@RequestMapping("/weather")
public class WeatherController {

    private final WeatherService weatherService;

    public WeatherController(WeatherService weatherService) {
        this.weatherService = weatherService;
    }

    @GetMapping("/{zip}")
    public int getWeather(@PathVariable String zip) {
        return weatherService.getWeather(zip);
    }
}

Behavior:

  1. First request for a ZIP code executes getWeather() (returns cached value after first call).
  2. Subsequent requests return cached results immediately.
  3. Scheduled method updateWeather() updates the cache every 10 seconds, ensuring fresh data without impacting users.

3. Advantages

  • Performance improvement: Users always get fast responses from cache.
  • Reduced API costs: Fewer calls to external services.
  • Controlled freshness: Scheduled updates ensure data is reasonably current.
  • Flexible eviction: @CacheEvict ensures stale data is removed when necessary.

4. Limitations

  • By default, @Cacheable doesn’t support automatic expiry. Redis TTL can be configured for this.
  • Limited flexibility for reactive streams and publisher types.
  • Full support for reactive programming may require additional utilities or custom implementations.

5. Real-World Analogy

  • Twitter feed, Netflix recommendations, or weather apps:
    • Cached results provide instant response.
    • Background services periodically update the cache.
    • Users experience consistent, fast performance without waiting for expensive computations.

This approach demonstrates a robust caching strategy combining @Cacheable, @CachePut, @CacheEvict, and scheduled updates for efficient, high-performance applications.