diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceClientConfigurationBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceClientConfigurationBuilderCustomizer.java index 750daecd9e2..1599a68310c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceClientConfigurationBuilderCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceClientConfigurationBuilderCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -23,7 +23,9 @@ import org.springframework.data.redis.connection.lettuce.LettuceClientConfigurat * Callback interface that can be implemented by beans wishing to customize the * {@link LettuceClientConfiguration} through a {@link LettuceClientConfigurationBuilder * LettuceClientConfiguration.LettuceClientConfigurationBuilder} whilst retaining default - * auto-configuration. + * auto-configuration. To customize only the + * {@link LettuceClientConfiguration#getClientOptions() client options} of the + * configuration, use {@link LettuceClientOptionsBuilderCustomizer} instead. * * @author Mark Paluch * @since 2.0.0 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..625d8b6864d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceClientOptionsBuilderCustomizer.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2024 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; +import io.lettuce.core.ClientOptions.Builder; + +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link ClientOptions} of the {@link LettuceClientConfiguration} through a + * {@link Builder} whilst retaining default auto-configuration. To customize the entire + * configuration, use {@link LettuceClientConfigurationBuilderCustomizer} instead. + * + * @author Soohyun Lim + * @since 3.4.0 + */ +@FunctionalInterface +public interface LettuceClientOptionsBuilderCustomizer { + + /** + * Customize the {@link Builder}. + * @param clientOptionsBuilder the builder to customize + */ + void customize(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..040eebd919c 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,22 @@ 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 +111,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 +130,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 +169,8 @@ 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 +190,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..4e0802ff589 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(); + assertThat(cf.getClientConfiguration().getClientOptions()) + .hasValueSatisfying((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)