Contradictory Cacheable And CachePut

ID

java.spring_cacheable_with_cacheput

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

best-practice, reliability, spring

Description

Reports a method or class reachable by both @Cacheable and @CachePut: on the same method, through a class-level annotation of the opposite kind, or on the class itself with no method involved.

Rationale

@Cacheable skips the method body and returns the cached value when one already exists for the given arguments. @CachePut always runs the method and stores the result, overwriting whatever was cached. A method under both annotations behaves unpredictably: whether the body runs at all depends on cache state rather than on the code calling it.

This is reported for four shapes: the two annotations on the same method; @CachePut on a method inside a @Cacheable class; @Cacheable on a method inside a @CachePut class; and the two annotations on the same class, with no method involved. Whether the annotations' condition() / unless() expressions happen to make the combination safe in a particular case is not evaluated — the combination is flagged regardless. Once a class carries both annotations, a method inside it that carries only one of them is not reported a second time — the class-level finding already covers it.

// Bad - contradictory: skips on hit, but also always overwrites
@Cacheable("books")
@CachePut("books")
public Book findBook(String isbn) {
  return repository.lookup(isbn);
}

Remediation

Keep only the annotation matching the intended behavior: @Cacheable to skip recomputation, or @CachePut to always refresh the cache.

// Good - one caching intent
@Cacheable("books")
public Book findBook(String isbn) {
  return repository.lookup(isbn);
}

© 2026 Xygeni. All rights reserved