是否可以调整 Caffeine LoadingCache 以与 Spring Boot 的 @Cacheable 一起使用?

Pau*_*enn 8 java spring caching spring-boot caffeine

我正在开发一个大型 Spring Boot 2.1.5.RELEASE 应用程序,使用 Caffeine 作为缓存提供程序。为了防止 I/O 瓶颈,我LoadingCache<K,V>在(基本上)以下方式中使用了咖啡因:

LoadingCache<K, V> cache = Caffeine.newBuilder()
    .refreshAfterWrite(1, TimeUnit.MINUTES)
    .build(loadStuffOverHttp());
Run Code Online (Sandbox Code Playgroud)

据我所知,如果不使用LoadingCache.

但是,LoadingCache 没有实现 Spring 的Cache. 这意味着我不能依赖@Bean方法来注册我的缓存,每个缓存都需要进行不同的配置。理论上,在 Spring 上下文中注册将允许它们与 Spring 的@Cacheable注释结合使用。

从我从CaffeineCacheConfiguration.java的源代码中可以看出,我也不能依赖 Spring Boot 的自动配置。类型的 Bean CaffeineCache(用于 Caffeine 的Spring 缓存适配器模式Cache<K,V>)是自动注册的,但适配器强制我将其<Object, Object>用作CacheLoader<K, V>. 我只想把这作为最后的手段。

这个 SO 问题表明可以以编程方式配置不同的缓存:

只需将您的自定义缓存公开为 bean。它们会自动添加到CaffeineCacheManager.

但是,使用LoadingCache<K, V>(使用任意K, V, not <Object, Object>)似乎更难。

这个 SO question似乎表明用 aSimpleCacheManager而不是 a来做它CaffeineCacheManager是可能的 - 但使用这个解决方案需要CacheLoader定义对Cachebean可用。这可能很容易首先需要使用缓存注入服务@Cacheable,例如在昂贵的 HTTP 调用的情况下。这似乎也是一个容易出现依赖循环的解决方案,但如果不是这种情况,请纠正我。

定义LoadingCache<K, V>与 Spring 一起使用的 Caffeine 的正确方法是@Cacheable什么?

小智 1

要定义 Caffeine LoadingCache<K, V> 以便与 Spring 的 @Cacheable 注释一起使用,您可以创建包装 Caffeine LoadingCache 的自定义 Cache 实现。这是一个示例实现:

import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import java.util.concurrent.Callable;
import com.github.benmanes.caffeine.cache.LoadingCache;

public class CaffeineCache implements Cache {

    private final String name;
    private final LoadingCache<Object, Object> cache;

    public CaffeineCache(String name, LoadingCache<Object, Object> cache) {
        this.name = name;
        this.cache = cache;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Object getNativeCache() {
        return cache;
    }

    @Override
    public ValueWrapper get(Object key) {
        Object value = cache.getIfPresent(key);
        return (value != null ? new SimpleValueWrapper(value) : null);
    }

    @Override
    public <T> T get(Object key, Class<T> type) {
        Object value = cache.getIfPresent(key);
        if (value != null && type != null && !type.isInstance(value)) {
            throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);
        }
        return (T) value;
    }

    @Override
    public void put(Object key, Object value) {
        cache.put(key, value);
    }

    @Override
    public void evict(Object key) {
        cache.invalidate(key);
    }

    @Override
    public void clear() {
        cache.invalidateAll();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Object key, Callable<T> valueLoader) {
        try {
            Object value = cache.get(key, k -> valueLoader.call());
            if (value != null && !value.getClass().isAssignableFrom(valueLoader.getClass())) {
                throw new IllegalStateException("Cached value is not of required type [" + valueLoader.getClass().getName() + "]: " + value);
            }
            return (T) value;
        } catch (Exception ex) {
            throw new ValueRetrievalException(key, valueLoader, ex);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以将缓存注册为 Spring bean,并将其与 @Cacheable 注释一起使用,如下所示:

@Bean
public Cache myCache() {
    LoadingCache<MyKey, MyValue> cache = Caffeine.newBuilder()
        .refreshAfterWrite(1, TimeUnit.MINUTES)
        .build(loadStuffOverHttp());
    return new CaffeineCache("myCache", cache);
}

@Cacheable("myCache")
public MyValue getValue(MyKey key) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

这种方法允许您将 Caffeine 缓存定义和配置为 LoadingCache<K, V>,同时仍然能够将其与 Spring 的缓存抽象一起使用。