I have this simple piece of code, in which I would like to cache a very expensive http call.
@RestController
class ReportController {
@Autowired
ReportService reportService;
@GetMapping("/getReport")
String getReport(@RequestParam String name) {
Report report = reportService.getReport(name);
return report.toString();
}
@Service
class ReportService {
private final ReportHttpInterface reportHttpInterface;
private final ReportLocalDecryptService reportLocalDecryptService;
ReportService(ReportHttpInterface reportHttpInterface, ReportLocalDecryptService reportLocalDecryptService) {
this.reportHttpInterface = reportHttpInterface;
this.reportLocalDecryptService = reportLocalDecryptService;
}
Report getReport(String name) {
String encryptedReport = reportHttpInterface.getReport(name);
String decryptedReport = reportLocalDecryptService.decryptWithLocalKey(encryptedReport);
return new Report(decryptedReport);
}
@HttpExchange(accept = MediaType.APPLICATION_JSON_VALUE)
interface ReportHttpInterface {
@Cacheable("report") //FLAG HERE!!!!!!!!!!!!!!!!!!!
@GetExchange("/api/veryexpensivecall/report")
String getReport(@RequestParam String name);
}
@Configuration
@EnableCaching
class ReportCacheConfiguration {
@Bean
CacheManagerCustomizer<CaffeineCacheManager> cacheManagerCustomizer(MeterRegistry registry) {
return cacheManager -> cacheManager.registerCustomCache("report", reportCache(registry));
}
private Cache<Object, Object> reportCache(MeterRegistry meterRegistry) {
return Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.recordStats(() -> new CaffeineStatsCounter(meterRegistry, "report"))
.build()
;
}
The http request to the external service here is very expensive, hence, I wish to cache this.
This is why you can see the cache configuration, as well as the @Cacheable("report")
in the HttpInterface class. Note, here, I am using the new HttpInterface construct, not using my own code to make the request with rest template nor rest client.
Issue:
I am being flagged on the line @Cacheable("report")
with
Spring doesn't recommend to annotate interface methods with @Cache* annotation
First of all, after some tests, "it seems to work fine". Meaning, the caching is working.
After the first expensive call, the subsequent calls are very fast, and the third party also confirmed there are no subsequent http requests. For instance, if I call this three times, the first time, it takes long, and the third party app can see my request. But for the second and third calls, it is fast, and the third party service confirmed they did not see anything.
If it is functionally correct, then what is this warning about?
How can I fix it?
Is there some functionality that is actually not working, but I missed?
According to the Spring documentation (search for annotating interfaces), annotating interface methods is a problem when using mode="aspectj"
to define aspects.
By default, Spring uses mode="proxy"
and your experiments show that caching is working.
But if you decide to switch to mode="aspectj"
in the future, caching will stop working. So, the warning makes sense.
If you are planning to migrate to AspectJ or if the warning concerns, I suggest introducing an additional layer before HttpExchange
and annotating the methods of this layer instead.
@Component
class CacheableReportHttpInterface {
private final ReportHttpInterface reportHttpInterface;
CacheableReportHttpInterface(ReportHttpInterface reportHttpInterface) {
this.reportHttpInterface = reportHttpInterface;
}
@Cacheable("report")
public String getReport(String name) {
return reportHttpInterface.getReport(name);
}
}