Add property to enable Caffeine async cache mode

CaffeineCacheManager supports asynchronous caches backed by Caffeine's
AsyncCache via setAsyncCacheMode, which adds support for Cache.retrieve.

Add a spring.cache.caffeine.async property (default false) wired to
CaffeineCacheManager.setAsyncCacheMode in CaffeineCacheConfiguration.

See gh-51844

Signed-off-by: seonghun lee <harrisleesh@gmail.com>
This commit is contained in:
seonghun lee
2026-09-28 10:12:51 +02:00
committed by Stéphane Nicoll
parent 0eed305ce0
commit 4e4cdc3fda
4 changed files with 43 additions and 0 deletions
@@ -223,6 +223,8 @@ If a javadoc:com.github.benmanes.caffeine.cache.CacheLoader[] bean is defined, i
Since the javadoc:com.github.benmanes.caffeine.cache.CacheLoader[] is going to be associated with _all_ caches managed by the cache manager, it must be defined as `CacheLoader<Object, Object>`.
The auto-configuration ignores any other generic type.
To expose asynchronous caches backed by Caffeine's javadoc:com.github.benmanes.caffeine.cache.AsyncCache[], adding support for `Cache.retrieve`, set configprop:spring.cache.caffeine.async[] to `true`.
[[io.caching.provider.cache2k]]
@@ -122,6 +122,12 @@ public class CacheProperties {
*/
private @Nullable String spec;
/**
* Whether to expose asynchronous caches backed by Caffeine's AsyncCache, adding
* support for Cache.retrieve.
*/
private boolean async;
public @Nullable String getSpec() {
return this.spec;
}
@@ -130,6 +136,14 @@ public class CacheProperties {
this.spec = spec;
}
public boolean isAsync() {
return this.async;
}
public void setAsync(boolean async) {
this.async = async;
}
}
/**
@@ -61,6 +61,7 @@ class CaffeineCacheConfiguration {
ObjectProvider<Caffeine<Object, Object>> caffeine, ObjectProvider<CaffeineSpec> caffeineSpec,
ObjectProvider<CacheLoader<Object, Object>> cacheLoader) {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setAsyncCacheMode(cacheProperties.getCaffeine().isAsync());
setCacheBuilder(cacheProperties, caffeineSpec.getIfAvailable(), caffeine.getIfAvailable(), cacheManager);
cacheLoader.ifAvailable(cacheManager::setCacheLoader);
return cacheManager;
@@ -80,6 +80,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
@@ -813,6 +814,31 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests {
.run(this::validateCaffeineCacheWithStats);
}
@Test
void caffeineCacheWithAsyncCacheMode() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=caffeine", "spring.cache.caffeine.async=true",
"spring.cache.cacheNames=foo")
.run((context) -> {
CaffeineCacheManager manager = getCacheManager(context, CaffeineCacheManager.class);
CaffeineCache foo = (CaffeineCache) manager.getCache("foo");
assertThat(foo).isNotNull();
assertThat(foo.getAsyncCache()).isNotNull();
});
}
@Test
void caffeineCacheWithoutAsyncCacheModeByDefault() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=caffeine", "spring.cache.cacheNames=foo")
.run((context) -> {
CaffeineCacheManager manager = getCacheManager(context, CaffeineCacheManager.class);
CaffeineCache foo = (CaffeineCache) manager.getCache("foo");
assertThat(foo).isNotNull();
assertThatIllegalStateException().isThrownBy(foo::getAsyncCache);
});
}
@Test
void autoConfiguredCacheManagerCanBeSwapped() {
this.contextRunner.withUserConfiguration(CacheManagerPostProcessorConfiguration.class)