5

I'm trying to add caching on my spring-boot app by following instruction on https://www.java4s.com/spring-boot-tutorials/how-to-configure-cache-in-spring-boot-applications/, but it doesn't work. I'm not entirely sure how to test it. I have a system.out.print under controller like in this article. If cache works then it will print "test" only once but return the same result from the request with same input. I have my code as below:

CurrencyController.java

    @RequestMapping(method = RequestMethod.POST)
    @Cacheable(value="currency")
    public ResponseEntity getExchangedCurrency(final @RequestBody CurrencyExchange currencyExchange) {
        System.out.println("Test");
        return ResponseEntity.ok()
              .headers(responseHeaders)
              .body(currencyService.calculate(currencyExchange));
    }

App.java

@SpringBootApplication
@EnableCaching
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);



    }
}

Chandara Chea
  • 289
  • 3
  • 11

1 Answers1

6

The way @Cacheable annotation works is utilizing method parameter as key by default for the caching map. In this case currencyExchange object. Since it's object on each request spring request dispatcher creates new object and cache manager keeps as separate key.

Req1 -> object1 -> map.put(object1, response)

Req2 -> object2 -> map.contains(object2) -> false -> map.put(object2, response)

If you think for all the post request send the same response from cache which doesn't the case always, you can change the key like this.

@RequestMapping(method = RequestMethod.POST)
@Cacheable(value="currency", key="#root.method")
public ResponseEntity getExchangedCurrency(final @RequestBody CurrencyExchange currencyExchange) {
    System.out.println("Test");
    return ResponseEntity.ok()
          .headers(responseHeaders)
          .body(currencyService.calculate(currencyExchange));
}

You can also use Spring Expression Language (SpEL) expression for defining key, if your currencyExchange has method getId() which can be used as potential cache key you can do like this

@Cacheable(value="currency", key="#root.args[0].getId()")

To clear cache Add @EnableCaching on spring boot main class, fixedDelay in milliseconds

@CacheEvict(allEntries = true, value = {"currency"})
@Scheduled(fixedDelay = 5000 ,  initialDelay = 5000)
public void clearCache() {
    System.out.println("Cache cleared");
}
Pasupathi Rajamanickam
  • 1,982
  • 1
  • 24
  • 48
  • Thank you for your reply. In CurrencyExchange there are 3 attributes: source, target and value. Is it possible to have the key that combine all these 3 values? I used this expression and it works but i'm not sure if this is the correct way. `@Cacheable(value="currency", key="#root.args[0].getSource() + #root.args[0].getTarget() + #root.args[0].getValue()")` . Another question is how can I make the cache expired in specified amount of time? @Pasupathi Rajamanickam – Chandara Chea Nov 20 '19 at 08:19
  • @ChandaraChea yes you can implement your key like you said, to clear cache, updated answer. – Pasupathi Rajamanickam Nov 20 '19 at 15:28
  • I am skeptical if the RequestMapping method would directly be cached. The reason is because Spring Cache works on the principle of building a Singleton Object of a Proxy class built on top of the current class having the @Caheable annotation. At runtime, I doubt if Spring would be able to invoke the Singleton Cache. Rather it may be creating an instance of your controller thereby skipping the cache. I think instead of caching RequestMapping methods, you should build cache on a separate method & invoke. Keep us posted on what you find. – Srini M Nov 23 '19 at 03:48
  • @SriniM you're right, if we are using `ehCache` spring-cache does work fine doing `@Cacheable` on `@RequestMapping` – Pasupathi Rajamanickam Nov 25 '19 at 15:34