I’m using Spring Boot 3.x, and the @Cacheable
annotation is working on methods that are not public. This is strange because the documentation clearly states:
Method visibility and cache annotations
When you use proxies, you should apply the cache annotations only to methods with public visibility. If you do annotate protected, private, or package-visible methods with these annotations, no error is raised, but the annotated method does not exhibit the configured caching settings. Consider using AspectJ (see the rest of this section) if you need to annotate non-public methods, as it changes the bytecode itself.
Here is my sample service:
@Service
public class CacheService {
@Cacheable("cache1")
String foo() {
System.out.println("string");
return "string";
}
}
And a very simple test:
@SpringBootTest
class ApplicationTests {
@Autowired
private CacheService cacheService;
@Test
void test2() {
cacheService.foo();
cacheService.foo();
cacheService.foo();
cacheService.foo();
cacheService.foo();
cacheService.foo();
}
}
In Spring Boot 2.7, the console output shows 6 “string” messages, but in 3.0.0 (and above), it only shows 1 “string”.
Do you have any idea what changed, and where in the documentation this behavior is described?
This came up during a company presentation, and I was confident I understood the behavior of this annotation, so I’m looking for arguments to justify it 🙂
EDIT
Here’s the example with branch spring-2.7
and spring-3.0
.
You are not using Spring’s default JDK interface proxies but are directly proxying a class, i.e. the result will be a CGLIB proxy, which basically is a subclass of the original class. I.e., non-public methods can and will be proxied and delegated to their original target methods, as long as they are not private. This is the expected behaviour for Spring proxies. For reference:
NOTE
Due to the proxy-based nature of Spring’s AOP framework, calls within the target object are, by definition, not intercepted. For JDK proxies, only public interface method calls on the proxy can be intercepted. With CGLIB, public and protected method calls on the proxy are intercepted (and even package-visible methods, if necessary). However, common interactions through proxies should always be designed through public signatures.
3