我希望在春季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