Skip to content

Commit dc4de06

Browse files
committed
Restore customization of the Couchbase cache manager
With the upgrade to the new Couchbase SDK and the related changes in Spring Data Couchbase, CacheManagerCustomizer can no longer be used to customize the Couchbase cache manager as it is an immutable class. This commit introduces a dedicated callback for the CouchbaseCacheManagerBuilder that is used by the auto-configuration and update the documentation to refer to it with a sample usage. Closes gh-22573
1 parent a9200b5 commit dc4de06

File tree

7 files changed

+127
-39
lines changed

7 files changed

+127
-39
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheConfigurations.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* @author Phillip Webb
2929
* @author Eddú Meléndez
3030
*/
31+
@SuppressWarnings("deprecation")
3132
final class CacheConfigurations {
3233

3334
private static final Map<CacheType, Class<?>> MAPPINGS;

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CouchbaseCacheConfiguration.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import com.couchbase.client.java.Cluster;
2323

24+
import org.springframework.beans.factory.ObjectProvider;
2425
import org.springframework.boot.autoconfigure.cache.CacheProperties.Couchbase;
2526
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2627
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -39,16 +40,20 @@
3940
*
4041
* @author Stephane Nicoll
4142
* @since 1.4.0
43+
* @deprecated since 2.3.3 as this class is not intended for public use. It will be made
44+
* package-private in a future release
4245
*/
4346
@Configuration(proxyBeanMethods = false)
4447
@ConditionalOnClass({ Cluster.class, CouchbaseClientFactory.class, CouchbaseCacheManager.class })
4548
@ConditionalOnMissingBean(CacheManager.class)
4649
@ConditionalOnSingleCandidate(CouchbaseClientFactory.class)
4750
@Conditional(CacheCondition.class)
51+
@Deprecated
4852
public class CouchbaseCacheConfiguration {
4953

5054
@Bean
5155
public CouchbaseCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers customizers,
56+
ObjectProvider<CouchbaseCacheManagerBuilderCustomizer> couchbaseCacheManagerBuilderCustomizers,
5257
CouchbaseClientFactory clientFactory) {
5358
List<String> cacheNames = cacheProperties.getCacheNames();
5459
CouchbaseCacheManagerBuilder builder = CouchbaseCacheManager.builder(clientFactory);
@@ -62,6 +67,7 @@ public CouchbaseCacheManager cacheManager(CacheProperties cacheProperties, Cache
6267
if (!ObjectUtils.isEmpty(cacheNames)) {
6368
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
6469
}
70+
couchbaseCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
6571
CouchbaseCacheManager cacheManager = builder.build();
6672
return customizers.customize(cacheManager);
6773
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.cache;
18+
19+
import org.springframework.data.couchbase.cache.CouchbaseCacheManager;
20+
import org.springframework.data.couchbase.cache.CouchbaseCacheManager.CouchbaseCacheManagerBuilder;
21+
22+
/**
23+
* Callback interface that can be implemented by beans wishing to customize the
24+
* {@link CouchbaseCacheManagerBuilder} before it is used to build the auto-configured
25+
* {@link CouchbaseCacheManager}.
26+
*
27+
* @author Stephane Nicoll
28+
* @since 2.3.3
29+
*/
30+
@FunctionalInterface
31+
public interface CouchbaseCacheManagerBuilderCustomizer {
32+
33+
/**
34+
* Customize the {@link CouchbaseCacheManagerBuilder}.
35+
* @param builder the builder to customize
36+
*/
37+
void customize(CouchbaseCacheManagerBuilder builder);
38+
39+
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.springframework.core.io.Resource;
6565
import org.springframework.data.couchbase.CouchbaseClientFactory;
6666
import org.springframework.data.couchbase.cache.CouchbaseCache;
67+
import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration;
6768
import org.springframework.data.couchbase.cache.CouchbaseCacheManager;
6869
import org.springframework.data.redis.cache.RedisCacheConfiguration;
6970
import org.springframework.data.redis.cache.RedisCacheManager;
@@ -195,7 +196,7 @@ void genericCacheExplicitWithCaches() {
195196

196197
@Test
197198
void couchbaseCacheExplicit() {
198-
this.contextRunner.withUserConfiguration(CouchbaseCacheConfiguration.class)
199+
this.contextRunner.withUserConfiguration(CouchbaseConfiguration.class)
199200
.withPropertyValues("spring.cache.type=couchbase").run((context) -> {
200201
CouchbaseCacheManager cacheManager = getCacheManager(context, CouchbaseCacheManager.class);
201202
assertThat(cacheManager.getCacheNames()).isEmpty();
@@ -204,14 +205,14 @@ void couchbaseCacheExplicit() {
204205

205206
@Test
206207
void couchbaseCacheWithCustomizers() {
207-
this.contextRunner.withUserConfiguration(CouchbaseCacheAndCustomizersConfiguration.class)
208+
this.contextRunner.withUserConfiguration(CouchbaseWithCustomizersConfiguration.class)
208209
.withPropertyValues("spring.cache.type=couchbase")
209210
.run(verifyCustomizers("allCacheManagerCustomizer", "couchbaseCacheManagerCustomizer"));
210211
}
211212

212213
@Test
213214
void couchbaseCacheExplicitWithCaches() {
214-
this.contextRunner.withUserConfiguration(CouchbaseCacheConfiguration.class)
215+
this.contextRunner.withUserConfiguration(CouchbaseConfiguration.class)
215216
.withPropertyValues("spring.cache.type=couchbase", "spring.cache.cacheNames[0]=foo",
216217
"spring.cache.cacheNames[1]=bar")
217218
.run((context) -> {
@@ -225,7 +226,7 @@ void couchbaseCacheExplicitWithCaches() {
225226

226227
@Test
227228
void couchbaseCacheExplicitWithTtl() {
228-
this.contextRunner.withUserConfiguration(CouchbaseCacheConfiguration.class)
229+
this.contextRunner.withUserConfiguration(CouchbaseConfiguration.class)
229230
.withPropertyValues("spring.cache.type=couchbase", "spring.cache.cacheNames=foo,bar",
230231
"spring.cache.couchbase.expiration=2000")
231232
.run((context) -> {
@@ -237,6 +238,20 @@ void couchbaseCacheExplicitWithTtl() {
237238
});
238239
}
239240

241+
@Test
242+
void couchbaseCacheWithCouchbaseCacheManagerBuilderCustomizer() {
243+
this.contextRunner.withUserConfiguration(CouchbaseConfiguration.class)
244+
.withPropertyValues("spring.cache.type=couchbase", "spring.cache.couchbase.expiration=15s")
245+
.withBean(CouchbaseCacheManagerBuilderCustomizer.class, () -> (builder) -> builder.cacheDefaults(
246+
CouchbaseCacheConfiguration.defaultCacheConfig().entryExpiry(java.time.Duration.ofSeconds(10))))
247+
.run((context) -> {
248+
CouchbaseCacheManager cacheManager = getCacheManager(context, CouchbaseCacheManager.class);
249+
CouchbaseCacheConfiguration couchbaseCacheConfiguration = getDefaultCouchbaseCacheConfiguration(
250+
cacheManager);
251+
assertThat(couchbaseCacheConfiguration.getExpiry()).isEqualTo(java.time.Duration.ofSeconds(10));
252+
});
253+
}
254+
240255
@Test
241256
void redisCacheExplicit() {
242257
this.contextRunner.withUserConfiguration(RedisConfiguration.class)
@@ -666,6 +681,10 @@ private void validateCaffeineCacheWithStats(AssertableApplicationContext context
666681
assertThat(((CaffeineCache) foo).getNativeCache().stats().missCount()).isEqualTo(1L);
667682
}
668683

684+
private CouchbaseCacheConfiguration getDefaultCouchbaseCacheConfiguration(CouchbaseCacheManager cacheManager) {
685+
return (CouchbaseCacheConfiguration) ReflectionTestUtils.getField(cacheManager, "defaultCacheConfig");
686+
}
687+
669688
private RedisCacheConfiguration getDefaultRedisCacheConfiguration(RedisCacheManager cacheManager) {
670689
return (RedisCacheConfiguration) ReflectionTestUtils.getField(cacheManager, "defaultCacheConfig");
671690
}
@@ -719,7 +738,7 @@ static class HazelcastCacheAndCustomizersConfiguration {
719738

720739
@Configuration(proxyBeanMethods = false)
721740
@EnableCaching
722-
static class CouchbaseCacheConfiguration {
741+
static class CouchbaseConfiguration {
723742

724743
@Bean
725744
CouchbaseClientFactory couchbaseClientFactory() {
@@ -729,8 +748,8 @@ CouchbaseClientFactory couchbaseClientFactory() {
729748
}
730749

731750
@Configuration(proxyBeanMethods = false)
732-
@Import({ CouchbaseCacheConfiguration.class, CacheManagerCustomizersConfiguration.class })
733-
static class CouchbaseCacheAndCustomizersConfiguration {
751+
@Import({ CouchbaseConfiguration.class, CacheManagerCustomizersConfiguration.class })
752+
static class CouchbaseWithCustomizersConfiguration {
734753

735754
}
736755

spring-boot-project/spring-boot-docs/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ dependencies {
8080
implementation("org.springframework:spring-test")
8181
implementation("org.springframework:spring-web")
8282
implementation("org.springframework:spring-webflux")
83+
implementation("org.springframework.data:spring-data-couchbase")
8384
implementation("org.springframework.data:spring-data-redis")
8485
implementation("org.springframework.data:spring-data-r2dbc")
8586
implementation("org.springframework.kafka:spring-kafka")

spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5083,49 +5083,24 @@ See https://github.com/infinispan/infinispan-spring-boot[Infinispan's documentat
50835083

50845084
[[boot-features-caching-provider-couchbase]]
50855085
==== Couchbase
5086-
If the https://www.couchbase.com/[Couchbase] Java client and the `couchbase-spring-cache` implementation are available and Couchbase is <<boot-features-couchbase,configured>>, a `CouchbaseCacheManager` is auto-configured.
5087-
It is also possible to create additional caches on startup by setting the configprop:spring.cache.cache-names[] property.
5088-
These caches operate on the `Bucket` that was auto-configured.
5089-
You can _also_ create additional caches on another `Bucket` by using the customizer.
5090-
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`.
5091-
You can create the first two caches through configuration, as follows:
5086+
If Spring Data Couchbase is available and Couchbase is <<boot-features-couchbase,configured>>, a `CouchbaseCacheManager` is auto-configured.
5087+
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.
5088+
For instance, the following configuration creates `cache1` and `cache2` caches with an entry _expiration_ of 10 minutes:
50925089

50935090
[source,properties,indent=0,configprops]
50945091
----
50955092
spring.cache.cache-names=cache1,cache2
5093+
spring.cache.couchbase.expiration=10m
50965094
----
50975095

5098-
Then you can define a `@Configuration` class to configure the extra `Bucket` and the `cache3` cache, as follows:
5096+
If you need more control over the configuration, consider registering a `CouchbaseCacheManagerBuilderCustomizer` bean.
5097+
The following example shows a customizer that configures a specific entry expiration for `cache1` and `cache2`:
50995098

51005099
[source,java,indent=0]
51015100
----
5102-
@Configuration(proxyBeanMethods = false)
5103-
public class CouchbaseCacheConfiguration {
5104-
5105-
private final Cluster cluster;
5106-
5107-
public CouchbaseCacheConfiguration(Cluster cluster) {
5108-
this.cluster = cluster;
5109-
}
5110-
5111-
@Bean
5112-
public Bucket anotherBucket() {
5113-
return this.cluster.openBucket("another", "secret");
5114-
}
5115-
5116-
@Bean
5117-
public CacheManagerCustomizer<CouchbaseCacheManager> cacheManagerCustomizer() {
5118-
return c -> {
5119-
c.prepareCache("cache3", CacheBuilder.newInstance(anotherBucket())
5120-
.withExpiration(2));
5121-
};
5122-
}
5123-
5124-
}
5101+
include::{code-examples}/cache/CouchbaseCacheManagerCustomizationExample.java[tag=configuration]
51255102
----
51265103

5127-
This sample configuration reuses the `Cluster` that was created through auto-configuration.
5128-
51295104

51305105

51315106
[[boot-features-caching-provider-redis]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.cache;
18+
19+
import java.time.Duration;
20+
21+
import org.springframework.boot.autoconfigure.cache.CouchbaseCacheManagerBuilderCustomizer;
22+
import org.springframework.context.annotation.Bean;
23+
import org.springframework.context.annotation.Configuration;
24+
import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration;
25+
26+
/**
27+
* An example how to customize {@code CouchbaseCacheManagerBuilder} via
28+
* {@code CouchbaseCacheManagerBuilderCustomizer}.
29+
*
30+
* @author Dmytro Nosan
31+
*/
32+
@Configuration(proxyBeanMethods = false)
33+
public class CouchbaseCacheManagerCustomizationExample {
34+
35+
// tag::configuration[]
36+
@Bean
37+
public CouchbaseCacheManagerBuilderCustomizer myCouchbaseCacheManagerBuilderCustomizer() {
38+
return (builder) -> builder
39+
.withCacheConfiguration("cache1",
40+
CouchbaseCacheConfiguration.defaultCacheConfig().entryExpiry(Duration.ofSeconds(10)))
41+
.withCacheConfiguration("cache2",
42+
CouchbaseCacheConfiguration.defaultCacheConfig().entryExpiry(Duration.ofMinutes(1)));
43+
44+
}
45+
// end::configuration[]
46+
47+
}

0 commit comments

Comments
 (0)