LoadingCache缓存声明、与@Cacheable关联及GuavaCache适配refreshAfterWrite问题
没问题,我来帮你逐个理清这些Guava Cache相关的问题:
1. 如何声明LoadingCache?
LoadingCache是Guava Cache提供的带自动加载能力的缓存实现,必须结合CacheBuilder和CacheLoader来声明。CacheLoader负责定义缓存未命中时的数据加载逻辑,示例代码如下:
// 声明一个键为Integer、值为List<Type>的LoadingCache LoadingCache<Integer, List<Type>> loadingCache = CacheBuilder.newBuilder() .maximumSize(1000) // 设置缓存的最大条目数 .refreshAfterWrite(5, TimeUnit.MINUTES) // 可选:设置自动刷新时间 .build(new CacheLoader<Integer, List<Type>>() { @Override public List<Type> load(Integer key) throws Exception { // 这里编写缓存未命中时的数据加载逻辑,比如从数据库/接口查询 return fetchDataFromDataSource(key); } });
核心点是必须实现CacheLoader的load方法,Guava会在缓存不存在对应键值时自动调用这个方法加载数据并填充到缓存中。
2. LoadingCache能否与@Cacheable方法关联?
当然可以!Spring的@Cacheable是基于Spring Cache抽象的注解,我们可以通过自定义CacheManager将Guava的LoadingCache整合进去,让@Cacheable方法使用LoadingCache作为底层缓存实现。
示例配置如下:
@Configuration @EnableCaching public class GuavaCacheConfig { @Bean public CacheManager cacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<>(); // 构建LoadingCache实例 LoadingCache<Integer, List<Type>> loadingCache = CacheBuilder.newBuilder() .maximumSize(1000) .refreshAfterWrite(5, TimeUnit.MINUTES) .build(new CacheLoader<Integer, List<Type>>() { @Override public List<Type> load(Integer key) throws Exception { return fetchDataFromDataSource(key); } }); // 将LoadingCache包装成Spring的GuavaCache并加入CacheManager caches.add(new GuavaCache("myCustomCache", loadingCache)); cacheManager.setCaches(caches); return cacheManager; } // 模拟数据加载方法,实际替换为你的业务逻辑 private List<Type> fetchDataFromDataSource(Integer key) { // 比如调用DAO查询数据库 return new ArrayList<>(); } }
配置完成后,你就可以在方法上使用@Cacheable(value = "myCustomCache"),Spring Cache会自动使用这个LoadingCache处理缓存逻辑,缓存未命中时自动触发CacheLoader的load方法加载数据。
3. 修改GuavaCache以支持refreshAfterWrite特性
你当前的代码创建的是普通Guava Cache,要支持refreshAfterWrite必须改用LoadingCache(因为自动刷新需要依赖CacheLoader重新加载数据)。修改后的代码如下:
public Cache createGuavaCache(String cacheName, long maxSize, long refreshAfterWriteInSeconds) { // 构建带有自动刷新能力的LoadingCache LoadingCache<Integer, List<Type>> loadingCache = CacheBuilder.newBuilder() .maximumSize(maxSize) .refreshAfterWrite(refreshAfterWriteInSeconds, TimeUnit.SECONDS) .build(new CacheLoader<Integer, List<Type>>() { @Override public List<Type> load(Integer key) throws Exception { // 实现你的数据加载逻辑,比如调用业务服务或DAO return yourDataLoadingService.loadData(key); } // 可选:重写reload方法实现异步刷新,避免阻塞读取请求 @Override public ListenableFuture<List<Type>> reload(Integer key, List<Type> oldValue) throws Exception { // 使用线程池异步加载新数据 return Executors.newSingleThreadExecutor().submit(() -> yourDataLoadingService.loadData(key)); } }); // 将LoadingCache包装成Spring的GuavaCache返回 return new GuavaCache(cacheName, loadingCache); }
需要注意几个关键点:
- 必须使用
CacheBuilder.build(CacheLoader)方法创建LoadingCache,而不是无参的build(),因为refreshAfterWrite依赖CacheLoader完成自动刷新。 - 泛型类型(
Integer和List<Type>)需要和你的实际缓存键值类型匹配,记得替换成业务对应的类型。 - 如果需要异步刷新,重写
reload方法可以让刷新操作在后台线程执行,不会阻塞当前的读取请求。
内容的提问来源于stack exchange,提问作者Joyal Augustine
相关产品推荐
相关产品推荐

