A Spring Boot 4.1.1 module whose test suite is the evidence for the article: 20 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. - The documented cache-provider detection order does not match CacheType's enum order in 4.1.1: COUCHBASE before INFINISPAN, and CACHE2K before CAFFEINE. - 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.
89 lines
3.7 KiB
Markdown
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.
|