diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceClientOptionsBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceClientOptionsBuilderCustomizer.java new file mode 100644 index 00000000000..288c50f7d0d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceClientOptionsBuilderCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2023 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.data.redis; + +import io.lettuce.core.ClientOptions; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link ClientOptions} through a {@link ClientOptions.Builder} whilst retaining default + * auto-configuration. + * + * @author Soohyun Lim + */ +@FunctionalInterface +public interface LettuceClientOptionsBuilderCustomizer { + + /** + * Customize the {@link ClientOptions.Builder}. + * @param clientOptionsBuilder the builder to customize + */ + void customize(ClientOptions.Builder clientOptionsBuilder); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java index c987df24b8e..4354d4cefd8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java @@ -88,18 +88,20 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration { @ConditionalOnMissingBean(RedisConnectionFactory.class) @ConditionalOnThreading(Threading.PLATFORM) LettuceConnectionFactory redisConnectionFactory( - ObjectProvider builderCustomizers, + ObjectProvider clientConfigurationBuilderCustomizers, + ObjectProvider clientOptionsBuilderCustomizers, ClientResources clientResources) { - return createConnectionFactory(builderCustomizers, clientResources); + return createConnectionFactory(clientConfigurationBuilderCustomizers, clientOptionsBuilderCustomizers, clientResources); } @Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) @ConditionalOnThreading(Threading.VIRTUAL) LettuceConnectionFactory redisConnectionFactoryVirtualThreads( - ObjectProvider builderCustomizers, + ObjectProvider clientConfigurationBuilderCustomizers, + ObjectProvider clientOptionsBuilderCustomizers, ClientResources clientResources) { - LettuceConnectionFactory factory = createConnectionFactory(builderCustomizers, clientResources); + LettuceConnectionFactory factory = createConnectionFactory(clientConfigurationBuilderCustomizers, clientOptionsBuilderCustomizers, clientResources); SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("redis-"); executor.setVirtualThreads(true); factory.setExecutor(executor); @@ -107,10 +109,11 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration { } private LettuceConnectionFactory createConnectionFactory( - ObjectProvider builderCustomizers, + ObjectProvider clientConfigurationBuilderCustomizers, + ObjectProvider clientOptionsBuilderCustomizers, ClientResources clientResources) { - LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources, - getProperties().getLettuce().getPool()); + LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(clientConfigurationBuilderCustomizers, clientOptionsBuilderCustomizers, + clientResources, getProperties().getLettuce().getPool()); return createLettuceConnectionFactory(clientConfig); } @@ -125,16 +128,17 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration { } private LettuceClientConfiguration getLettuceClientConfiguration( - ObjectProvider builderCustomizers, + ObjectProvider clientConfigurationBuilderCustomizers, + ObjectProvider clientOptionsBuilderCustomizers, ClientResources clientResources, Pool pool) { LettuceClientConfigurationBuilder builder = createBuilder(pool); applyProperties(builder); if (StringUtils.hasText(getProperties().getUrl())) { customizeConfigurationFromUrl(builder); } - builder.clientOptions(createClientOptions()); + builder.clientOptions(createClientOptions(clientOptionsBuilderCustomizers)); builder.clientResources(clientResources); - builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); + clientConfigurationBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } @@ -163,7 +167,7 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration { } } - private ClientOptions createClientOptions() { + private ClientOptions createClientOptions(ObjectProvider clientConfigurationBuilderCustomizers) { ClientOptions.Builder builder = initializeClientOptionsBuilder(); Duration connectTimeout = getProperties().getConnectTimeout(); if (connectTimeout != null) { @@ -183,7 +187,9 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration { } builder.sslOptions(sslOptionsBuilder.build()); } - return builder.timeoutOptions(TimeoutOptions.enabled()).build(); + builder.timeoutOptions(TimeoutOptions.enabled()); + clientConfigurationBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); + return builder.build(); } private ClientOptions.Builder initializeClientOptionsBuilder() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java index 8e6dc52f51f..3e41e25a20b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java @@ -127,6 +127,8 @@ class RedisAutoConfigurationTests { this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> { LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); assertThat(cf.isUseSsl()).isTrue(); + cf.getClientConfiguration().getClientOptions().ifPresent(options -> + assertThat(options.isAutoReconnect()).isFalse()); }); } @@ -638,6 +640,11 @@ class RedisAutoConfigurationTests { return LettuceClientConfigurationBuilder::useSsl; } + @Bean + LettuceClientOptionsBuilderCustomizer clientOptionsBuilderCustomizer() { + return builder -> builder.autoReconnect(false); + } + } @Configuration(proxyBeanMethods = false)