我需要对弹簧缓存功能有一些基本的了解。我想构建一个缓存加热器和resyncer作为计划bean,以便在发生更改时按需更新缓存。
假设我有AccountClient
getAccount(String id)
,它从非常慢的API中获取Account
。所以基本上我可以做到
@Cachable(cacheNames = "account", key = "#id", sync = true)
public Account getAccount(String id) {
//...
}
一切正常。现在我想要预热缓存,此外,我得到了一个getNewOrChangedAccounts()
,它从我的慢速数据存储中检索已更改帐户的ID。
所以这是我的方法:
public class AccountCacheManager {
//...
@Scheduled(initialDelay = 3000L, fixedRate = 10000L)
public void sync() {
List<Account> modifiedAccounts = accountClient.getNewOrChangedAccounts();
modifiedAccounts.getAccounts().parallelStream()
.forEach(account -> {
//delete old entry
evictAccount(account.getId());
//store new entry
putAccount(account.getId());
});
log.info("finished resync");
}
@CacheEvict(cacheNames = "account", key = "#id")
public void evictAccount(String id) {
log.debug("evicting account {}", id);
}
@CachePut(cacheNames = "account", key = "#id")
public void putAccount(String id) {
log.debug("storing account {}", id);
accountService.getAccount(id);
}
}
所以我可以证明,这个过程开始并做了一些事情。但是,当我点击我的API时,即使我的同步超过了后端的所有条目,我看到第一个命中转到慢速后端。
我觉得我误解了Spring缓存API的一些细节,所以我该如何实现呢?
答案 0 :(得分:1)
来自@CachePut
的{{1}}:
与@Cacheable注释相反,此注释不会 导致建议的方法被跳过。相反,它总是导致 要调用的方法及其结果存储在关联的 高速缓存中。
因此,使用@CachePut
注释的方法应该将数据返回到缓存:
@CachePut(cacheNames = "account", key = "#id")
public Account putAccount(String id) {
log.debug("storing account {}", id);
return accountService.getAccount(id);
}
另请参阅此问题:documentation
答案 1 :(得分:0)
在后台,当在类上声明@Cacheable
或@CachePut
批注时,Spring将创建一个代理类,该代理类将包装从该类创建的bean实例。
此抽象缓存层将只能通过该类的外部中的任何方法调用使用。
解决上述问题的简单方法是将cron方法分为不同的类,然后通过注入的bean调用缓存的方法。
@Component
public class AccountCacheManager {
//...
@CachePut(cacheNames = "account", key = "#id")
public Account putAccount(String id) {
log.debug("storing account {}", id);
return accountService.getAccount(id);
}
}
@Component
public class CacheWarmupManager {
// bean injected here is actually a proxy that wrapped AccountCacheManager with cache implementation
@Autowired private AccountCacheManager accountManager;
@Scheduled(initialDelay = 3000L, fixedRate = 10000L)
public void sync() {
List<Account> modifiedAccounts = accountClient.getNewOrChangedAccounts();
modifiedAccounts.getAccounts().parallelStream()
.forEach(account -> {
// force update Cache with new Entry
accountManager.putAccount(account.getId());
});
log.info("finished resync");
}
}
注意:
您的缓存预热cron方法不起作用的原因是该方法位于所缓存方法的相同类上。因此,无论何时cron触发,它都会直接调用putAccount
和evictAccount
方法,而不会传递提供缓存的代理类。