Files
spring-boot-demo/caching/docs/02-the-three-annotations.md
T
asmhatre 66208bcd97 Add caching: the Spring cache abstraction, keys, eviction timing and the self-invocation trap
A Spring Boot 4.1.1 module whose test suite is the evidence for the article: 19 tests
producing 22 transcripts under docs/output/, plus 12 documentation chapters.

Findings the build pins:
- @EnableCaching has no exposeProxy attribute; the widely-copied
  @EnableCaching(exposeProxy = true) does not compile.
- Two methods sharing a cache name and an argument type share a key space, and one
  silently serves the other's answers.
- beforeInvocation = true is NOT deferred by TransactionAwareCacheManagerProxy on
  7.0.9 - doEvict picks evictIfPresent, which the decorator does not intercept.
- Four of five invalid declarations start a clean context and throw at the first call.
- Caffeine on the classpath silently displaces the simple provider.
2026-09-12 05:19:22 +00:00

89 lines
3.7 KiB
Markdown

[← what caching is](01-what-caching-is.md) · [next: self-invocation →](03-self-invocation.md)
# 2. The annotations, attribute by attribute
Verified against `spring-context` 7.0.9 with `javap`, so this is what the class files declare
rather than what the documentation summarises.
## `@Cacheable`
| Attribute | Type | Notes |
|---|---|---|
| `value` / `cacheNames` | `String[]` | Aliases. Several names means several caches are consulted and all of them written. |
| `key` | `String` | SpEL. Mutually exclusive with `keyGenerator`; setting both fails the context at startup. |
| `keyGenerator` | `String` | Bean name of a `KeyGenerator`. |
| `cacheManager` | `String` | Bean name, for when there is more than one. |
| `cacheResolver` | `String` | Full control over which caches this operation uses. Mutually exclusive with `cacheManager`. |
| `condition` | `String` | SpEL, evaluated **before** the call. False means no lookup and no write. |
| `unless` | `String` | SpEL, evaluated **after**. Can see `#result`. Vetoes the write only. |
| `sync` | `boolean` | One caller computes, the rest wait. Heavily restricted — see [chapter 7](07-sync-and-async.md). |
## `@CachePut`
Same attributes minus `sync`. Always invokes the method, always writes the result. Use it when
you already have the new value and want to avoid the miss that an eviction would cause.
Do not put `@CachePut` and `@Cacheable` on the same method. The framework does not stop you, and
the two have opposite intentions.
## `@CacheEvict`
Same as `@Cacheable` minus `unless` and `sync`, plus:
| Attribute | Type | Notes |
|---|---|---|
| `allEntries` | `boolean` | Clears the whole region in one operation instead of key by key. |
| `beforeInvocation` | `boolean` | Default `false` — evict after a *successful* return. See [chapter 5](05-eviction.md). |
`void` is fine here; the annotation is a trigger and the return value is ignored.
## `@Caching`
A container for several operations of the same type on one method:
```java
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0") })
public Book importBooks(String deposit, Date date) { ... }
```
## `@CacheConfig`
Class-level defaults for `cacheNames`, `keyGenerator`, `cacheManager` and `cacheResolver`. It
enables nothing on its own. Precedence runs global (`CachingConfigurer`) → class
(`@CacheConfig`) → operation, with the operation always winning.
## `@EnableCaching`
Exactly three attributes, and this is worth knowing because a popular piece of advice uses a
fourth that does not exist:
```
AdviceMode mode()
int order()
boolean proxyTargetClass()
```
There is no `exposeProxy`. `@EnableCaching(exposeProxy = true)` does not compile.
`docs/output/04-non-public-and-postconstruct.txt` prints the reflected attribute list.
Spring Boot's reference documentation advises against putting `@EnableCaching` on the main
application class, because it makes caching mandatory for every test slice too. Put it on a
`@Configuration` class you can exclude.
## Declarations that are rejected, and when
`docs/output/20-invalid-declarations.txt` runs five bad declarations through a real context. Only
the first fails at startup:
| Declaration | Fails |
|---|---|
| `key` and `keyGenerator` together | at startup |
| `sync = true` with `unless` | at the first call |
| `sync = true` across two caches | at the first call |
| `sync = true` combined with another cache operation | at the first call |
| a cache name not in `spring.cache.cache-names` | at the first call |
Four of the five start a healthy-looking application and throw on a code path that may not run
for hours. That is the single strongest argument for having a test that actually calls each
cached method.