Polish "Add support for cache2k in memory caching"

See gh-28498
This commit is contained in:
Stephane Nicoll
2022-03-21 09:28:44 +01:00
parent 774f61fcb5
commit a2959bbcf2
12 changed files with 120 additions and 114 deletions

View File

@@ -19,14 +19,19 @@ package org.springframework.boot.autoconfigure.cache;
import org.cache2k.Cache2kBuilder;
/**
* Default configuration for cache2k when Spring boot auto configuration is creating the
* Cache Manager.
* Callback interface that can be implemented by beans wishing to customize the default
* setup for caches added to the manager via addCaches and for dynamically created caches.
*
* @author Jens Wilke
* @author Stephane Nicoll
* @since 2.7.0
*/
public interface Cache2kDefaults {
public interface Cache2kBuilderCustomizer {
/**
* Customize the default cache settings.
* @param builder the builder to customize
*/
void customize(Cache2kBuilder<?, ?> builder);
}

View File

@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.cache;
import java.util.Collection;
import java.util.function.Function;
import org.cache2k.Cache2kBuilder;
import org.cache2k.extra.spring.SpringCache2kCacheManager;
@@ -34,6 +35,7 @@ import org.springframework.util.CollectionUtils;
* Cache2k cache configuration.
*
* @author Jens Wilke
* @author Stephane Nicoll
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Cache2kBuilder.class, SpringCache2kCacheManager.class })
@@ -43,15 +45,9 @@ class Cache2kCacheConfiguration {
@Bean
SpringCache2kCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers customizers,
ObjectProvider<Cache2kDefaults> defaults) {
ObjectProvider<Cache2kBuilderCustomizer> cache2kBuilderCustomizers) {
SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager();
Cache2kDefaults specifiedDefaults = defaults.getIfAvailable();
if (specifiedDefaults != null) {
cacheManager.defaultSetup((builder) -> {
specifiedDefaults.customize(builder);
return builder;
});
}
cacheManager.defaultSetup(configureDefaults(cache2kBuilderCustomizers));
Collection<String> cacheNames = cacheProperties.getCacheNames();
if (!CollectionUtils.isEmpty(cacheNames)) {
cacheManager.setDefaultCacheNames(cacheNames);
@@ -59,4 +55,12 @@ class Cache2kCacheConfiguration {
return customizers.customize(cacheManager);
}
private Function<Cache2kBuilder<?, ?>, Cache2kBuilder<?, ?>> configureDefaults(
ObjectProvider<Cache2kBuilderCustomizer> cache2kBuilderCustomizers) {
return (builder) -> {
cache2kBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
};
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.cache;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import javax.cache.Caching;
import javax.cache.configuration.CompleteConfiguration;
@@ -48,6 +49,7 @@ import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfigurati
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
@@ -74,7 +76,6 @@ 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.assertThatCode;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
@@ -624,137 +625,60 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests {
}
@Test
void cache2kCacheWithDynamicCacheCreation() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k").run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).isEmpty();
assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault");
Cache foo = manager.getCache("dynamic");
foo.get("1");
});
}
@Test
void cache2kCacheWithCacheNamesInProperties() {
void cache2kCacheWithExplicitCaches() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k", "spring.cache.cacheNames=foo,bar").run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("foo", "bar");
assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault");
manager.getCache("foo").get("1");
manager.getCache("bar").get("2");
assertThat(manager.getCache("unknown")).isNull();
});
}
@Test
void cache2kCacheWithDynamicCacheCreationAndDefaults() {
void cache2kCacheWithCustomizedDefaults() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k")
.withBean(Cache2kDefaults.class, () -> (b) -> b.valueType(String.class).loader((key) -> "default"))
.withBean(Cache2kBuilderCustomizer.class,
() -> (builder) -> builder.valueType(String.class).loader((key) -> "default"))
.run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).isEmpty();
assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault");
Cache foo = manager.getCache("fooDynamic");
assertThat(foo.get("1").get()).isEqualTo("default");
Cache bar = manager.getCache("barDynamic");
assertThat(bar.get("1").get()).isEqualTo("default");
assertThat(manager.getCache("barDynamic")).isSameAs(bar);
Cache dynamic = manager.getCache("dynamic");
assertThat(dynamic.get("1")).satisfies(hasEntry("default"));
assertThat(dynamic.get("2")).satisfies(hasEntry("default"));
});
}
@Test
void cache2kCacheWithNamesInPropertiesAndDefaults() {
void cache2kCacheWithCustomizedDefaultsAndExplicitCaches() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k", "spring.cache.cacheNames=foo,bar")
.withBean(Cache2kDefaults.class, () -> (b) -> b.valueType(String.class).loader((key) -> "default"))
.withBean(Cache2kBuilderCustomizer.class,
() -> (builder) -> builder.valueType(String.class).loader((key) -> "default"))
.run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("foo", "bar");
assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault");
Cache foo = manager.getCache("foo");
assertThat(foo.get("1").get()).isEqualTo("default");
Cache bar = manager.getCache("bar");
assertThat(bar.get("1").get()).isEqualTo("default");
assertThat(manager.getCache("foo").get("1")).satisfies(hasEntry("default"));
assertThat(manager.getCache("bar").get("1")).satisfies(hasEntry("default"));
});
}
@Test
void cache2kCacheWithDynamicCacheCreationAndDefaultAndCustomCachesViaCacheManagerCustomizer() {
void cache2kCacheWithCacheManagerCustomizer() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k").withBean(CacheManagerCustomizer.class,
() -> (CacheManagerCustomizer<SpringCache2kCacheManager>) (cm) -> {
cm.defaultSetup((b) -> b.valueType(String.class).loader((key) -> "default"));
cm.addCache("custom", (b) -> b.valueType(String.class).loader((key) -> "custom"));
})
.withPropertyValues("spring.cache.type=cache2k")
.withBean(CacheManagerCustomizer.class,
() -> cache2kCacheManagerCustomizer((cacheManager) -> cacheManager.addCache("custom",
(builder) -> builder.valueType(String.class).loader((key) -> "custom"))))
.run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("custom");
assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault");
Cache foo = manager.getCache("fooDynamic");
assertThat(foo.get("1").get()).isEqualTo("default");
Cache custom = manager.getCache("custom");
assertThat(custom.get("1").get()).isEqualTo("custom");
assertThat(manager.getCache("custom").get("1")).satisfies(hasEntry("custom"));
});
}
@Test
void cache2kCacheWithNamesInPropertiesAndDefaultsAndCacheManagerCustomizer() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k",
"spring.cache.cacheNames=foo,bar")
.withBean(Cache2kDefaults.class, () -> (b) -> b.valueType(String.class).loader((key) -> "default"))
.withBean(CacheManagerCustomizer.class,
() -> (CacheManagerCustomizer<SpringCache2kCacheManager>) (cm) -> cm.addCache("custom",
(b) -> b.valueType(String.class).loader((key) -> "custom")))
.run((context) -> {
SpringCache2kCacheManager manager = getCacheManager(context, SpringCache2kCacheManager.class);
assertThat(manager.getCacheNames()).containsExactlyInAnyOrder("foo", "bar", "custom");
assertThat(manager.getNativeCacheManager().getName()).isEqualTo("springDefault");
Cache foo = manager.getCache("foo");
assertThat(foo.get("1").get()).isEqualTo("default");
Cache bar = manager.getCache("bar");
assertThat(bar.get("1").get()).isEqualTo("default");
assertThat(manager.isAllowUnknownCache()).isFalse();
});
}
/**
* Default cannot be changed in CacheManagerCustomizer, if cache names are present in
* the properties
*/
@Test
void cache2kCacheNamesAndCacheManagerDefaultsYieldsException() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k", "spring.cache.cacheNames=foo,bar")
.withBean(CacheManagerCustomizer.class,
() -> (CacheManagerCustomizer<SpringCache2kCacheManager>) (cm) -> cm
.defaultSetup((b) -> b.entryCapacity(1234)))
.run((context) -> {
assertThatCode(() -> getCacheManager(context, SpringCache2kCacheManager.class)).getRootCause()
.isInstanceOf(IllegalStateException.class);
// close underlying cache manager directly since Spring bean was not
// created
org.cache2k.CacheManager.getInstance(SpringCache2kCacheManager.DEFAULT_SPRING_CACHE_MANAGER_NAME)
.close();
});
}
/**
* Applying defaults via customizer and cache manager is not possible
*/
@Test
void cache2kCacheDefaultsTwiceYieldsException() {
this.contextRunner.withUserConfiguration(DefaultCacheConfiguration.class)
.withPropertyValues("spring.cache.type=cache2k")
.withBean(Cache2kDefaults.class, () -> (b) -> b.entryCapacity(1234))
.withBean(CacheManagerCustomizer.class,
() -> (CacheManagerCustomizer<SpringCache2kCacheManager>) (cm) -> cm
.defaultSetup((b) -> b.entryCapacity(1234)))
.run((context) -> assertThatCode(() -> getCacheManager(context, SpringCache2kCacheManager.class))
.getRootCause().isInstanceOf(IllegalStateException.class));
private CacheManagerCustomizer<SpringCache2kCacheManager> cache2kCacheManagerCustomizer(
Consumer<SpringCache2kCacheManager> cacheManager) {
return cacheManager::accept;
}
@Test
@@ -818,6 +742,10 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests {
});
}
private Consumer<ValueWrapper> hasEntry(Object value) {
return (valueWrapper) -> assertThat(valueWrapper.get()).isEqualTo(value);
}
private void validateCaffeineCacheWithStats(AssertableApplicationContext context) {
CaffeineCacheManager manager = getCacheManager(context, CaffeineCacheManager.class);
assertThat(manager.getCacheNames()).containsOnly("foo", "bar");