缓存更新场景:Cache-Control:max-age=0时的缓存处理方案咨询
Got it, let's tackle this cache refresh scenario step by step. Your current setup uses @Cacheable with a condition, but we need to adjust it to handle the max-age=0 case where we bypass cache, fetch fresh data, and update the cache afterward.
We need to handle two distinct scenarios with Spring Cache annotations:
- Scenario 1:
cacheControlequalsmax-age=0→ Bypass cache reading, fetch fresh data from the underlying service, and force-update the cache - Scenario 2:
cacheControlis null or any other value → Prioritize reading from cache; only fetch from the service and write to cache if the cache is empty
Spring Cache's @Cacheable handles "read-first" logic, while @CachePut handles "write-after-execution" logic (it always runs the method and updates the cache). We can combine these two with conditional checks to meet your requirements.
Step 1: Refine Utility Class Methods
First, update your Utils class to add a method that explicitly identifies the force-refresh case (max-age=0):
public class Utils { // Original method: Returns true when caching is allowed (not max-age=0) public static boolean isCachingAllowed(String cacheControl) { return cacheControl == null || !"max-age=0".equals(cacheControl); } // New method: Returns true when we need to force-refresh the cache public static boolean isForceRefresh(String cacheControl) { return "max-age=0".equals(cacheControl); } // Your existing idKey method public static String idKey(String id) { return "user_data_" + id; } }
Step 2: Combine Annotations with @Caching
Use @Caching to wrap @Cacheable and @CachePut, each with their own conditional logic:
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Caching; @Caching( cacheable = @Cacheable( key = "T(com.org.Utils).idKey(#id)", condition = "T(com.org.Utils).isCachingAllowed(#cacheControl)" ), put = @CachePut( key = "T(com.org.Utils).idKey(#id)", condition = "T(com.org.Utils).isForceRefresh(#cacheControl)" ) ) public UserData get(String id, String cacheControl) { // Delegate to your underlying service here return underlyingUserDataService.fetchById(id); }
How It Works
Let's break down the logic for each scenario:
- When
cacheControl = "max-age=0":@Cacheable's condition is false → Skips reading from cache entirely@CachePut's condition is true → Executes the method (fetches fresh data from the service), then updates the cache with the newUserData
- When
cacheControlis null or any other value:@Cacheable's condition is true → First tries to read from cache- If cache hits: Returns cached data immediately, skips the method body
- If cache misses: Executes the method to fetch data, then automatically writes it to cache (default
@Cacheablebehavior)
@CachePut's condition is false → No action taken
Additional Notes
- If you need to handle other cache-control directives like
no-cache, extend theisForceRefreshmethod:public static boolean isForceRefresh(String cacheControl) { return cacheControl != null && (cacheControl.contains("max-age=0") || cacheControl.contains("no-cache")); } - Verify your cache provider (Redis, Caffeine, etc.) is properly configured, and ensure the
idKeymethod generates unique keys for each user record - Test edge cases: First-time calls (empty cache), cache hits with
max-age=0, and regular cache reads to confirm behavior matches expectations
内容的提问来源于stack exchange,提问作者paramupk

