diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java index 0228326998..6cf569f4df 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java @@ -28,6 +28,7 @@ import org.springframework.util.Assert; * @author Phillip Webb * @author EddĂș MelĂ©ndez */ +@SuppressWarnings("deprecation") final class CacheConfigurations { private static final Map> MAPPINGS; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CouchbaseCacheConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CouchbaseCacheConfiguration.java index 99c1891c72..12028d2f2c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CouchbaseCacheConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CouchbaseCacheConfiguration.java @@ -21,6 +21,7 @@ import java.util.List; import com.couchbase.client.java.Cluster; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.cache.CacheProperties.Couchbase; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -39,16 +40,20 @@ import org.springframework.util.ObjectUtils; * * @author Stephane Nicoll * @since 1.4.0 + * @deprecated since 2.3.3 as this class is not intended for public use. It will be made + * package-private in a future release */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Cluster.class, CouchbaseClientFactory.class, CouchbaseCacheManager.class }) @ConditionalOnMissingBean(CacheManager.class) @ConditionalOnSingleCandidate(CouchbaseClientFactory.class) @Conditional(CacheCondition.class) +@Deprecated public class CouchbaseCacheConfiguration { @Bean public CouchbaseCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers customizers, + ObjectProvider couchbaseCacheManagerBuilderCustomizers, CouchbaseClientFactory clientFactory) { List cacheNames = cacheProperties.getCacheNames(); CouchbaseCacheManagerBuilder builder = CouchbaseCacheManager.builder(clientFactory); @@ -62,6 +67,7 @@ public class CouchbaseCacheConfiguration { if (!ObjectUtils.isEmpty(cacheNames)) { builder.initialCacheNames(new LinkedHashSet<>(cacheNames)); } + couchbaseCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); CouchbaseCacheManager cacheManager = builder.build(); return customizers.customize(cacheManager); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CouchbaseCacheManagerBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CouchbaseCacheManagerBuilderCustomizer.java new file mode 100644 index 0000000000..9dfca195be --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CouchbaseCacheManagerBuilderCustomizer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2019 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.cache; + +import org.springframework.data.couchbase.cache.CouchbaseCacheManager; +import org.springframework.data.couchbase.cache.CouchbaseCacheManager.CouchbaseCacheManagerBuilder; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link CouchbaseCacheManagerBuilder} before it is used to build the auto-configured + * {@link CouchbaseCacheManager}. + * + * @author Stephane Nicoll + * @since 2.3.3 + */ +@FunctionalInterface +public interface CouchbaseCacheManagerBuilderCustomizer { + + /** + * Customize the {@link CouchbaseCacheManagerBuilder}. + * @param builder the builder to customize + */ + void customize(CouchbaseCacheManagerBuilder builder); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java index ec86125d84..482093f79a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java @@ -65,6 +65,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.data.couchbase.CouchbaseClientFactory; import org.springframework.data.couchbase.cache.CouchbaseCache; +import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration; import org.springframework.data.couchbase.cache.CouchbaseCacheManager; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; @@ -196,7 +197,7 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests { @Test void couchbaseCacheExplicit() { - this.contextRunner.withUserConfiguration(CouchbaseCacheConfiguration.class) + this.contextRunner.withUserConfiguration(CouchbaseConfiguration.class) .withPropertyValues("spring.cache.type=couchbase").run((context) -> { CouchbaseCacheManager cacheManager = getCacheManager(context, CouchbaseCacheManager.class); assertThat(cacheManager.getCacheNames()).isEmpty(); @@ -205,14 +206,14 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests { @Test void couchbaseCacheWithCustomizers() { - this.contextRunner.withUserConfiguration(CouchbaseCacheAndCustomizersConfiguration.class) + this.contextRunner.withUserConfiguration(CouchbaseWithCustomizersConfiguration.class) .withPropertyValues("spring.cache.type=couchbase") .run(verifyCustomizers("allCacheManagerCustomizer", "couchbaseCacheManagerCustomizer")); } @Test void couchbaseCacheExplicitWithCaches() { - this.contextRunner.withUserConfiguration(CouchbaseCacheConfiguration.class) + this.contextRunner.withUserConfiguration(CouchbaseConfiguration.class) .withPropertyValues("spring.cache.type=couchbase", "spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar") .run((context) -> { @@ -226,7 +227,7 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests { @Test void couchbaseCacheExplicitWithTtl() { - this.contextRunner.withUserConfiguration(CouchbaseCacheConfiguration.class) + this.contextRunner.withUserConfiguration(CouchbaseConfiguration.class) .withPropertyValues("spring.cache.type=couchbase", "spring.cache.cacheNames=foo,bar", "spring.cache.couchbase.expiration=2000") .run((context) -> { @@ -238,6 +239,20 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests { }); } + @Test + void couchbaseCacheWithCouchbaseCacheManagerBuilderCustomizer() { + this.contextRunner.withUserConfiguration(CouchbaseConfiguration.class) + .withPropertyValues("spring.cache.type=couchbase", "spring.cache.couchbase.expiration=15s") + .withBean(CouchbaseCacheManagerBuilderCustomizer.class, () -> (builder) -> builder.cacheDefaults( + CouchbaseCacheConfiguration.defaultCacheConfig().entryExpiry(java.time.Duration.ofSeconds(10)))) + .run((context) -> { + CouchbaseCacheManager cacheManager = getCacheManager(context, CouchbaseCacheManager.class); + CouchbaseCacheConfiguration couchbaseCacheConfiguration = getDefaultCouchbaseCacheConfiguration( + cacheManager); + assertThat(couchbaseCacheConfiguration.getExpiry()).isEqualTo(java.time.Duration.ofSeconds(10)); + }); + } + @Test void redisCacheExplicit() { this.contextRunner.withUserConfiguration(RedisConfiguration.class) @@ -668,6 +683,10 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests { assertThat(((CaffeineCache) foo).getNativeCache().stats().missCount()).isEqualTo(1L); } + private CouchbaseCacheConfiguration getDefaultCouchbaseCacheConfiguration(CouchbaseCacheManager cacheManager) { + return (CouchbaseCacheConfiguration) ReflectionTestUtils.getField(cacheManager, "defaultCacheConfig"); + } + private RedisCacheConfiguration getDefaultRedisCacheConfiguration(RedisCacheManager cacheManager) { return (RedisCacheConfiguration) ReflectionTestUtils.getField(cacheManager, "defaultCacheConfig"); } @@ -721,7 +740,7 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests { @Configuration(proxyBeanMethods = false) @EnableCaching - static class CouchbaseCacheConfiguration { + static class CouchbaseConfiguration { @Bean CouchbaseClientFactory couchbaseClientFactory() { @@ -731,8 +750,8 @@ class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationTests { } @Configuration(proxyBeanMethods = false) - @Import({ CouchbaseCacheConfiguration.class, CacheManagerCustomizersConfiguration.class }) - static class CouchbaseCacheAndCustomizersConfiguration { + @Import({ CouchbaseConfiguration.class, CacheManagerCustomizersConfiguration.class }) + static class CouchbaseWithCustomizersConfiguration { } diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index 82960047ee..b98212df48 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -80,6 +80,7 @@ dependencies { implementation("org.springframework:spring-test") implementation("org.springframework:spring-web") implementation("org.springframework:spring-webflux") + implementation("org.springframework.data:spring-data-couchbase") implementation("org.springframework.data:spring-data-redis") implementation("org.springframework.data:spring-data-r2dbc") implementation("org.springframework.kafka:spring-kafka") diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc index 5b9a15146e..1e182c011e 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc @@ -5163,49 +5163,24 @@ See https://github.com/infinispan/infinispan-spring-boot[Infinispan's documentat [[boot-features-caching-provider-couchbase]] ==== Couchbase -If the https://www.couchbase.com/[Couchbase] Java client and the `couchbase-spring-cache` implementation are available and Couchbase is <>, a `CouchbaseCacheManager` is auto-configured. -It is also possible to create additional caches on startup by setting the configprop:spring.cache.cache-names[] property. -These caches operate on the `Bucket` that was auto-configured. -You can _also_ create additional caches on another `Bucket` by using the customizer. -Assume you need two caches (`cache1` and `cache2`) on the "main" `Bucket` and one (`cache3`) cache with a custom time to live of 2 seconds on the "`another`" `Bucket`. -You can create the first two caches through configuration, as follows: +If Spring Data Couchbase is available and Couchbase is <>, a `CouchbaseCacheManager` is auto-configured. +It is possible to create additional caches on startup by setting the configprop:spring.cache.cache-names[] property and cache defaults can be configured by using `spring.cache.couchbase.*` properties. +For instance, the following configuration creates `cache1` and `cache2` caches with an entry _expiration_ of 10 minutes: [source,properties,indent=0,configprops] ---- spring.cache.cache-names=cache1,cache2 + spring.cache.couchbase.expiration=10m ---- -Then you can define a `@Configuration` class to configure the extra `Bucket` and the `cache3` cache, as follows: +If you need more control over the configuration, consider registering a `CouchbaseCacheManagerBuilderCustomizer` bean. +The following example shows a customizer that configures a specific entry expiration for `cache1` and `cache2`: [source,java,indent=0] ---- - @Configuration(proxyBeanMethods = false) - public class CouchbaseCacheConfiguration { - - private final Cluster cluster; - - public CouchbaseCacheConfiguration(Cluster cluster) { - this.cluster = cluster; - } - - @Bean - public Bucket anotherBucket() { - return this.cluster.openBucket("another", "secret"); - } - - @Bean - public CacheManagerCustomizer cacheManagerCustomizer() { - return c -> { - c.prepareCache("cache3", CacheBuilder.newInstance(anotherBucket()) - .withExpiration(2)); - }; - } - - } +include::{code-examples}/cache/CouchbaseCacheManagerCustomizationExample.java[tag=configuration] ---- -This sample configuration reuses the `Cluster` that was created through auto-configuration. - [[boot-features-caching-provider-redis]] diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/cache/CouchbaseCacheManagerCustomizationExample.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/cache/CouchbaseCacheManagerCustomizationExample.java new file mode 100644 index 0000000000..612a955c2c --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/cache/CouchbaseCacheManagerCustomizationExample.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.docs.cache; + +import java.time.Duration; + +import org.springframework.boot.autoconfigure.cache.CouchbaseCacheManagerBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration; + +/** + * An example how to customize {@code CouchbaseCacheManagerBuilder} via + * {@code CouchbaseCacheManagerBuilderCustomizer}. + * + * @author Dmytro Nosan + */ +@Configuration(proxyBeanMethods = false) +public class CouchbaseCacheManagerCustomizationExample { + + // tag::configuration[] + @Bean + public CouchbaseCacheManagerBuilderCustomizer myCouchbaseCacheManagerBuilderCustomizer() { + return (builder) -> builder + .withCacheConfiguration("cache1", + CouchbaseCacheConfiguration.defaultCacheConfig().entryExpiry(Duration.ofSeconds(10))) + .withCacheConfiguration("cache2", + CouchbaseCacheConfiguration.defaultCacheConfig().entryExpiry(Duration.ofMinutes(1))); + + } + // end::configuration[] + +}