1

I have a Spring controller and want to cache the response. When I move the @Cacheable annotation from the getBooks to the doGetBooks method, the caching will stop. Once I move it back to the getBooks method caching works again. Why is that and how can I fix it?

This will cache the public method response

@GetMapping
@Cacheable(value = "cache", key = "{ #root.methodName }")
public Books getBooks(@RequestHeader(value = "user-agent", required = false) String userAgent) throws Exception {
     if(valid) {
          return this.doGetBooks();
     }
     throw new Exception();
}


public Books doGetBooks() throws Exception{
    ...

This will never cache the private method response

@GetMapping
public Books getBooks(@RequestHeader(value = "user-agent", required = false) String userAgent) throws Exception {
     if(valid) {
          return this.getBooks();
     }
     throw new Exception();
}


@Cacheable(value = "cache", key = "{ #root.methodName }")
public Books doGetBooks() throws Exception{
    ...
hansi
  • 2,278
  • 6
  • 34
  • 42
  • I think you have a typo in the second code block (which never calls doGetBooks(). However the root of the caching problem has to deal with Spring cache capturing the proxy to getBooks(), but can't capture the doGetBooks() method. I will try to find a link on how to work around this but likely someone will faster. – Ian Mc Jan 23 '18 at 18:43
  • You should read this section... https://docs.spring.io/spring/docs/5.0.2.RELEASE/spring-framework-reference/core.html#aop-understanding-aop-proxies – John Blum Jan 24 '18 at 04:57

1 Answers1

3

Problem: You are calling doGetBooks() within the same class, and Spring cache requires an AOP proxy to the called method.

This is a good discussion describing why Spring AOP can not intercept methods called by other class methods: AOP calling methods within methods

There are at least three workarounds:

  1. Refactor the code: Move doGetBooks() into another @Component, and invoke the method using that (injected) bean (refactoredBean.doGetBooks())
  2. Create a self-reference to the service invoking the call (By @Autowired private MyService myservice and invoke myservice.doGetBooks().
  3. Using the ApplicationContext to cast the service bean, and invoking the method on that bean.

Once you invoke a method that Spring Cache can intercept (via AOP), then the @Cacheable() annotation should trigger.

Ian Mc
  • 5,656
  • 4
  • 18
  • 25