Spring启动缓存预热

时间:2017-04-21 14:19:33

标签: java spring spring-boot spring-cache

我需要对弹簧缓存功能有一些基本的了解。我想构建一个缓存加热器和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的一些细节,所以我该如何实现呢?

2 个答案:

答案 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触发,它都会直接调用putAccountevictAccount方法,而不会传递提供缓存的代理类