Spring Cache Caffeine 批量检索

dev*_*123 6 spring spring-boot spring-cache caffeine-cache

是否可以将咖啡因CacheLoader::loadAll@Cacheable带有集合参数的带注释方法一起使用,例如

@Cacheable(cacheNames = "exampleCache", cacheManager="exampleCacheManager", keyGenerator = "complexKeyGenerator")
   List<String> getItems(List<String> keys, String commonForEveryKey) {
      return ...
}

@Component
class ComplexKeyGenerator implements KeyGenerator {

      @Override
      public Object generate(Object target, Method method, Object... params) {
         return ((List<String>)params[0]).stream()
                     .map(item -> new ComplexKey(item, (String) params[1]))
                     .collect(Collectors.toList());
      }
}

@Data
   @AllArgsConstructor
   class ComplexKey {
      String key;
      String commonGuy;
}

class CustomCacheLoader implements CacheLoader<ComplexKey, String> {

      @Override
      public @Nullable String load(@NonNull ComplexKey key) throws Exception {
         return loadAll(List.of(key)).get(key);
      }

      @Override
      public @NonNull Map<@NonNull ComplexKey, @NonNull String> loadAll(@NonNull Iterable<? extends @NonNull ComplexKey> keys)
         throws Exception {
         return ...
      }
}

@Bean
   CacheManager exampleCacheManager(LoadingCache exampleCache) {
      CaffeineCacheManager cacheManager = new CaffeineCacheManager();
      cacheManager.registerCustomCache("exampleCache", exampleCache());
      return cacheManager;
}

   @Bean
   Cache<Object, Object> exampleCache() {
      return Caffeine.newBuilder()
                     .maximumSize(1000)
                     .expireAfterWrite(1, TimeUnit.HOURS)
                     .recordStats()
                     .build(new CustomCacheLoader());
}
Run Code Online (Sandbox Code Playgroud)

看起来 Spring Cache 调用CustomCacheLoader::load而不是CustomCacheLoader::loadAll调用并失败ClassCastException,因为它无法将键集合转换为单个键。

我还应该配置什么才能使其正常工作?

dek*_*ard 1

不幸的是,Spring 不支持通过@Cacheable机制通过键集合检索缓存项的集合。

这是一个问题:https://github.com/spring-projects/spring-framework/issues/23221

实现此目的的一种选择是使用提供注释的自定义库( https://github.com/qaware/collection-cacheable-for-spring@CollectionCacheable ) :

@CollectionCacheable(cacheNames = "myCache")
Map<Long, MyEntity> findByIds(Collection<Long> ids) {
    // do efficient batch retrieve of many MyEntity's and build result map
}
Run Code Online (Sandbox Code Playgroud)

如果您真的想坚持使用现有的代码,您可以将其概括为类似的内容:

@Component
class ComplexKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        if (params.length < 2 || !(params[0] instanceof Collection && params[1] instanceof String)) {
            return SimpleKeyGenerator.generateKey(params);
        }

        return ((Collection<String>) params[0]).stream()
                .map(item -> new ComplexKey(item, (String) params[1]))
                .collect(Collectors.toList());
    }
}

class CustomCacheLoader implements CacheLoader<Object, Object> {
    @Override
    public Object load(Object key) throws Exception {
        final Collection<Object> keys = (key instanceof Collection) ?
                ((Collection<Object>) key) : Collections.singletonList(key);
        final Collection<Object> values = new ArrayList<>(loadAll(keys).values());
        return values;
    }

    @Override
    public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)