多个咖啡因加载缓存添加到Spring CaffeineCacheManager



我希望在春季CacheManager中添加几个不同的LoadingCache,但是我不明白如何使用CaffeineCacheManager来实现这一点。 似乎只有一个加载器可以刷新内容,但是每个缓存都需要单独的加载器。 是否可以将多个加载缓存添加到 Spring 缓存管理器? 如果是这样,那又如何呢?

CaffeineCacheManager cacheManage = new CaffeineCacheManager();
LoadingCache<String, Optional<Edition>> loadingCache1 = 
            Caffeine.newBuilder()
            .maximumSize(150)
            .refreshAfterWrite(5, TimeUnit.MINUTES)
            .build(test -> this.testRepo.find(test));
LoadingCache<String, Optional<Edition>> loadingCache2 = 
            Caffeine.newBuilder()
            .maximumSize(150)
            .refreshAfterWrite(5, TimeUnit.MINUTES)
            .build(test2 -> this.testRepo.find2(test2));
// How do I add to cache manager, and specify a name?

是的,这是可能的。由于您需要微调每个缓存,因此您可能更擅长自己定义它们。回到您的示例,下一步将是:

SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
    new CaffeineCache("first", loadingCache1),
    new CaffeineCache("second", loadingCache2)));

然后您可以像往常一样使用它,例如

@Cacheable("first")
public Foo load(String id) { ... }

如果您使用的是 Spring Boot,您只需将单个缓存公开为 bean(因此org.springframework.cache.Cache实现(,我们将检测它们并自动为您创建SimpleCacheManager

请注意,此策略允许您将缓存抽象与不同的实现一起使用。 first可以是咖啡因缓存,也可以是来自其他提供商的second缓存。

拥有这个类将允许您像往常一样在您想要的地方使用@Cacheable("cacheA")

@EnableCaching
@Configuration
public class CacheConfiguration {
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.registerCustomCache("cacheA", defaultCache());
        manager.registerCustomCache("cacheB", bigCache());
        manager.registerCustomCache("cacheC", longCache());
        // to avoid dynamic caches and be sure each name is assigned to a specific config (dynamic = false)
        // throws error when tries to use a new cache
        manager.setCacheNames(Collections.emptyList());
        return manager;
    }
    private static Cache<Object, Object> defaultCache() {
        return Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .build();
    }
    private static Cache<Object, Object> bigCache() {
        return Caffeine.newBuilder()
                .maximumSize(5000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .build();
    }
    private static Cache<Object, Object> longCache() {
        return Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(1, TimeUnit.HOURS)
                .build();
    }
}

感谢您@rado,这是他答案的改进版本。这样,我们可以直接从应用程序属性配置缓存

cache:
  specs:
    big-cache:
      expire-after: WRITE
      timeout: 2h
      max-size: 1000
    long-cache:
      expire-after: ACCESS
      timeout: 30d
      max-size: 100

为此我们需要一个缓存属性

@Data
@EnableConfigurationProperties
@Configuration
@ConfigurationProperties(prefix = "cache")
public class CacheProperties {
    private static final int DEFAULT_CACHE_SIZE = 100;
    private Map<String, CacheSpec> specs = new HashMap<>();
    @Data
    public static class  CacheSpec {
        private Duration timeout;
        private Integer maxSize = DEFAULT_CACHE_SIZE;
        private ExpireAfter expireAfter = ExpireAfter.WRITE;
    }
    enum ExpireAfter { WRITE, ACCESS }
}

然后我们可以直接从外部配置文件进行配置

@EnableCaching
@Configuration
@RequiredArgsConstructor
public class CacheConfiguration {
    private final CacheProperties cacheProperties;
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        Map<String, CacheProperties.CacheSpec> specs = cacheProperties.getSpecs();
        specs.keySet().forEach(cacheName -> {
            CacheProperties.CacheSpec spec = specs.get(cacheName);
            manager.registerCustomCache(cacheName, buildCache(spec));
        });
        // to avoid dynamic caches and be sure each name is assigned
        // throws error when tries to use a new cache
        manager.setCacheNames(Collections.emptyList());
        return manager;
    }
    private Cache<Object, Object> buildCache(CacheProperties.CacheSpec cacheSpec) {
        if (cacheSpec.getExpireAfter() == CacheProperties.ExpireAfter.ACCESS) {
            return Caffeine.newBuilder()
                    .expireAfterAccess(cacheSpec.getTimeout())
                    .build();
        }
        return Caffeine.newBuilder()
                .expireAfterWrite(cacheSpec.getTimeout())
                .build();
    }
}

现在,您可以将缓存与使用缓存名称一起使用

    @Cacheable(cacheNames = "big-cache", key = "{#key}", unless="#result == null")
    public Object findByKeyFromBigCache(String key) {
        // create the required object and return
    }
    @Cacheable(cacheNames = "long-cache", key = "{#key}", unless="#result == null")
    public Object findByKeyFromLongCache(String key) {
        // create the required object and return
    }

必须使用com.github.benmanes.caffeine.cache.Ticker构建自定义咖啡因缓存。

这是一个使用 Java 17、Spring Boot 2.7.7 和 Caffeine 3.1.6 测试的工作示例,其中我们配置了一个过期时间为 60 秒的 cacheOne 和一个在一小时或 3600 秒后过期的 cacheTwo:

@Configuration
public class CacheConfig {
    @Bean
    public CacheManager cacheManagerTicker(Ticker ticker) {
        var cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(List.of(
                this.buildCache("cacheOne", ticker, 1, 60, TimeUnit.SECONDS),
                this.buildCache("cacheTwo", ticker, 1, 3600, TimeUnit.SECONDS)
        ));
        return cacheManager;
    }
    private CaffeineCache buildCache(String cacheName, Ticker ticker,
                                     int maxSize, int expireAfterWrite, TimeUnit timeUnit) {
        Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder();
        if (expireAfterWrite > 0) {
            cacheBuilder.expireAfterWrite(expireAfterWrite, timeUnit);
        }
        if (maxSize > 0) {
            cacheBuilder.maximumSize(maxSize);
        }
        cacheBuilder.ticker(ticker);
        return new CaffeineCache(cacheName, cacheBuilder.build());
    }
    @Bean
    public Ticker ticker() {
        return Ticker.systemTicker();
    }
}

此示例改编自使用 Spring 和 Caffeine 定义多个缓存配置,Ben Manes 指出有一个名为 Coffee Boots 的适配器,它具有请求的行为:https://github.com/stepio/coffee-boots

最新更新