$npx -y skills add ducpm2303/claude-java-plugins --skill java-cacheUse when the user asks to add caching, configure Redis or Caffeine cache, use @Cacheable/@CacheEvict/@CachePut, optimize repeated database or API calls, or review existing Spring Boot cache configuration.
| 1 | # Spring Cache Skill |
| 2 | |
| 3 | Detect the cache provider in use, then apply the correct patterns. |
| 4 | |
| 5 | ## Step 1 — Detect setup |
| 6 | |
| 7 | Check `pom.xml` or `build.gradle`: |
| 8 | - `spring-boot-starter-data-redis` → Redis (Lettuce client by default) |
| 9 | - `spring-boot-starter-cache` + `caffeine` → Caffeine (in-process) |
| 10 | - `spring-boot-starter-cache` only → Simple (ConcurrentHashMap, dev only) |
| 11 | - None present → offer to add (recommend Caffeine for single-instance, Redis for multi-instance/distributed) |
| 12 | |
| 13 | Check Spring Boot version: |
| 14 | - Boot 3.x → Lettuce 6.x, Caffeine 3.x |
| 15 | - Boot 2.x → Lettuce 5.x, Caffeine 2.x/3.x |
| 16 | |
| 17 | --- |
| 18 | |
| 19 | ## Mode: `review` |
| 20 | |
| 21 | User asks to review existing cache configuration. Check for: |
| 22 | |
| 23 | - [ ] `@EnableCaching` present on a `@Configuration` class — missing it silently disables all cache annotations |
| 24 | - [ ] Cache names declared in `application.yml` with explicit TTL — no unnamed or unbounded caches |
| 25 | - [ ] `@Cacheable` methods are on Spring-managed beans (not `private`, not called within the same class — proxy bypass) |
| 26 | - [ ] Cache keys are deterministic — `@Cacheable(key = "#id")` not `#root.methodName` unless intentional |
| 27 | - [ ] `@CacheEvict` present wherever data is mutated — missing eviction causes stale cache |
| 28 | - [ ] `@CachePut` used to update cache on write — not a `@CacheEvict` + re-fetch pattern |
| 29 | - [ ] Redis serialization configured — default JDK serialization is not readable/portable; use `GenericJackson2JsonRedisSerializer` |
| 30 | - [ ] Redis TTL set — without `time-to-live`, entries never expire |
| 31 | - [ ] Caffeine `maximumSize` set — without it, cache grows unbounded and causes OOM |
| 32 | - [ ] Null values handled — `@Cacheable(unless = "#result == null")` to avoid caching nulls |
| 33 | - [ ] Cache metrics exposed: `management.metrics.cache.instrument=true` |
| 34 | |
| 35 | --- |
| 36 | |
| 37 | ## Mode: `setup` |
| 38 | |
| 39 | User asks to add caching from scratch. |
| 40 | |
| 41 | ### Caffeine (recommended for single-instance apps) |
| 42 | 1. Add `spring-boot-starter-cache` + `com.github.ben-manes.caffeine:caffeine` |
| 43 | 2. Add `@EnableCaching` to a `@Configuration` class |
| 44 | 3. Configure cache specs in `application.yml` — set `maximum-size` and `expire-after-write` |
| 45 | 4. Annotate service methods with `@Cacheable`, `@CacheEvict`, `@CachePut` |
| 46 | |
| 47 | ### Redis (recommended for multi-instance / distributed) |
| 48 | 1. Add `spring-boot-starter-data-redis` |
| 49 | 2. Configure `spring.data.redis.host/port` (Boot 3.x) or `spring.redis.host/port` (Boot 2.x) |
| 50 | 3. Configure `RedisCacheConfiguration` bean — set TTL and use `GenericJackson2JsonRedisSerializer` |
| 51 | 4. Add `@EnableCaching` and annotate service methods |
| 52 | |
| 53 | See `references/patterns.md` for full configuration examples. |
| 54 | |
| 55 | --- |
| 56 | |
| 57 | ## Mode: `cacheable` |
| 58 | |
| 59 | User asks to cache the result of a method. |
| 60 | |
| 61 | 1. Place `@Cacheable(cacheNames = "products", key = "#id")` on the service method |
| 62 | 2. The method must be on a Spring-managed bean and not `private` |
| 63 | 3. The method must not call itself (proxy bypass) — extract to a separate bean if needed |
| 64 | 4. Add `unless = "#result == null"` to avoid caching null results |
| 65 | 5. Ensure the return type is `Serializable` (Redis) or any object (Caffeine) |
| 66 | 6. Add a corresponding `@CacheEvict` on the update/delete method |
| 67 | |
| 68 | --- |
| 69 | |
| 70 | ## Mode: `evict` |
| 71 | |
| 72 | User asks to invalidate/evict cache entries on data changes. |
| 73 | |
| 74 | - `@CacheEvict(cacheNames = "products", key = "#id")` — evict a single entry on update/delete |
| 75 | - `@CacheEvict(cacheNames = "products", allEntries = true)` — evict all entries (use sparingly) |
| 76 | - `@CacheEvict(beforeInvocation = true)` — evict before method runs (use when method may throw) |
| 77 | - For multi-cache eviction: `@Caching(evict = { @CacheEvict("products"), @CacheEvict("productList") })` |
| 78 | |
| 79 | --- |
| 80 | |
| 81 | ## Mode: `redis` |
| 82 | |
| 83 | User asks specifically for Redis cache configuration. |
| 84 | |
| 85 | 1. Add `spring-boot-starter-data-redis` |
| 86 | 2. Configure connection: `spring.data.redis.host`, `spring.data.redis.port`, `spring.data.redis.password` |
| 87 | 3. Define `RedisCacheManager` bean with: |
| 88 | - `GenericJackson2JsonRedisSerializer` for values (human-readable, portable) |
| 89 | - `StringRedisSerializer` for keys |
| 90 | - Default TTL + per-cache TTL overrides |
| 91 | 4. Enable `@EnableCaching` |
| 92 | 5. For Redis Cluster: set `spring.data.redis.cluster.nodes` |
| 93 | 6. For Redis Sentinel: set `spring.data.redis.sentinel.master` and `nodes` |
| 94 | |
| 95 | --- |
| 96 | |
| 97 | ## Output format |
| 98 | |
| 99 | For **review mode**: list findings as `[CRITICAL] / [HIGH] / [MEDIUM] / [LOW]` with file:line references. |
| 100 | |
| 101 | For **implementation modes**: show exact Maven/Gradle dependency, full `application.yml` block, and complete Java configuration + annotated example. State minimum Spring Boot version where it differs. |