From ac651442fa82817a13654208328c3e45188fa305 Mon Sep 17 00:00:00 2001 From: bono007 Date: Tue, 4 Aug 2020 23:51:24 -0500 Subject: [PATCH 1/2] Add support for selecting the Redis client to use See gh-22569 --- .../redis/JedisConnectionConfiguration.java | 5 +++- .../redis/LettuceConnectionConfiguration.java | 3 ++ .../data/redis/RedisProperties.java | 29 +++++++++++++++++++ .../RedisAutoConfigurationJedisTests.java | 6 ++++ .../redis/RedisAutoConfigurationTests.java | 21 +++++++++++++- 5 files changed, 62 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java index ff47497500..dd16409c62 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java @@ -26,6 +26,7 @@ import redis.clients.jedis.JedisPoolConfig; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; @@ -42,9 +43,12 @@ import org.springframework.util.StringUtils; * * @author Mark Paluch * @author Stephane Nicoll + * @author Chris Bono */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class }) +@ConditionalOnMissingBean(RedisConnectionFactory.class) +@ConditionalOnProperty(name = "spring.redis.client-type", havingValue = "jedis", matchIfMissing = true) class JedisConnectionConfiguration extends RedisConnectionConfiguration { JedisConnectionConfiguration(RedisProperties properties, @@ -54,7 +58,6 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration { } @Bean - @ConditionalOnMissingBean(RedisConnectionFactory.class) JedisConnectionFactory redisConnectionFactory( ObjectProvider builderCustomizers) throws UnknownHostException { return createJedisConnectionFactory(builderCustomizers); 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 dca260f473..1084b8b312 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 @@ -31,6 +31,7 @@ import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Lettuce.Cluster.Refresh; import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool; import org.springframework.context.annotation.Bean; @@ -49,9 +50,11 @@ import org.springframework.util.StringUtils; * * @author Mark Paluch * @author Andy Wilkinson + * @author Chris Bono */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisClient.class) +@ConditionalOnProperty(name = "spring.redis.client-type", havingValue = "lettuce", matchIfMissing = true) class LettuceConnectionConfiguration extends RedisConnectionConfiguration { LettuceConnectionConfiguration(RedisProperties properties, diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java index c762541a1d..c1c87ed211 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java @@ -76,6 +76,11 @@ public class RedisProperties { */ private String clientName; + /** + * Type of client to use. + */ + private ClientType clientType = ClientType.Lettuce; + private Sentinel sentinel; private Cluster cluster; @@ -148,6 +153,14 @@ public class RedisProperties { this.clientName = clientName; } + public ClientType getClientType() { + return this.clientType; + } + + public void setClientType(ClientType clientType) { + this.clientType = clientType; + } + public Sentinel getSentinel() { return this.sentinel; } @@ -172,6 +185,22 @@ public class RedisProperties { return this.lettuce; } + /** + * Type of Redis client to use. + */ + public enum ClientType { + + /** + * Use the Lettuce client + */ + Lettuce, + + /** + * Use the Jedis client + */ + Jedis + } + /** * Pool properties. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java index 9bc153a49f..0aa6c515d2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java @@ -42,6 +42,12 @@ class RedisAutoConfigurationJedisTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)); + @Test + void testDefaultRedisConfiguration() { + this.contextRunner.run((context) -> assertThat(context.getBean("redisConnectionFactory")) + .isInstanceOf(JedisConnectionFactory.class)); + } + @Test void testOverrideRedisConfiguration() { this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1").run((context) -> { 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 5a1424d21d..4b50cbcd9e 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 @@ -39,6 +39,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisSentinelConfiguration; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; @@ -52,7 +53,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** - * Tests for {@link RedisAutoConfiguration}. + * Tests for {@link RedisAutoConfiguration} when both Jedis and Lettuce are on the + * classpath. * * @author Dave Syer * @author Christian Dupuis @@ -74,6 +76,8 @@ class RedisAutoConfigurationTests { this.contextRunner.run((context) -> { assertThat(context.getBean("redisTemplate", RedisOperations.class)).isNotNull(); assertThat(context.getBean(StringRedisTemplate.class)).isNotNull(); + assertThat(context.getBean("redisConnectionFactory")).isInstanceOf(LettuceConnectionFactory.class); + assertThat(context.getBeanProvider(JedisConnectionConfiguration.class).getIfAvailable()).isNull(); }); } @@ -182,6 +186,21 @@ class RedisAutoConfigurationTests { }); } + @Test + void testRedisConfigurationWithClientNameJedis() { + this.contextRunner.withPropertyValues("spring.redis.client-type:jedis") + .run((context) -> assertThat(context.getBean("redisConnectionFactory")) + .isInstanceOf(JedisConnectionFactory.class)); + } + + @Test + void testRedisConfigurationWithClientNameLettuce() { + this.contextRunner.withPropertyValues("spring.redis.client-type:lettuce").run((context) -> { + assertThat(context.getBean("redisConnectionFactory")).isInstanceOf(LettuceConnectionFactory.class); + assertThat(context.getBeanProvider(JedisConnectionConfiguration.class).getIfAvailable()).isNull(); + }); + } + @Test void testRedisConfigurationWithSentinel() { List sentinels = Arrays.asList("127.0.0.1:26379", "127.0.0.1:26380"); From 589669d0cc49f05381cdf947e69804aa4d720a0d Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 5 Aug 2020 09:20:19 +0200 Subject: [PATCH 2/2] Polish "Add support for selecting the Redis client to use" See gh-22569 --- .../redis/JedisConnectionConfiguration.java | 3 +-- .../redis/LettuceConnectionConfiguration.java | 1 - .../data/redis/RedisAutoConfiguration.java | 5 +++- .../data/redis/RedisProperties.java | 13 ++++----- .../RedisAutoConfigurationJedisTests.java | 11 ++++++-- .../redis/RedisAutoConfigurationTests.java | 27 ++++++++++--------- 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java index dd16409c62..91331a5e9d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * 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. @@ -43,7 +43,6 @@ import org.springframework.util.StringUtils; * * @author Mark Paluch * @author Stephane Nicoll - * @author Chris Bono */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class }) 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 1084b8b312..f49acdbfe9 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 @@ -50,7 +50,6 @@ import org.springframework.util.StringUtils; * * @author Mark Paluch * @author Andy Wilkinson - * @author Chris Bono */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisClient.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java index ab7d05f323..9ba9fe5891 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * 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. @@ -21,6 +21,7 @@ import java.net.UnknownHostException; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -52,6 +53,7 @@ public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") + @ConditionalOnSingleCandidate(RedisConnectionFactory.class) public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate template = new RedisTemplate<>(); @@ -61,6 +63,7 @@ public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean + @ConditionalOnSingleCandidate(RedisConnectionFactory.class) public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java index c1c87ed211..d8c12b6148 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java @@ -77,9 +77,9 @@ public class RedisProperties { private String clientName; /** - * Type of client to use. + * Type of client to use. By default, auto-detected according to the classpath. */ - private ClientType clientType = ClientType.Lettuce; + private ClientType clientType; private Sentinel sentinel; @@ -191,14 +191,15 @@ public class RedisProperties { public enum ClientType { /** - * Use the Lettuce client + * Use the Lettuce redis client. */ - Lettuce, + LETTUCE, /** - * Use the Jedis client + * Use the Jedis redis client. */ - Jedis + JEDIS + } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java index 0aa6c515d2..3e23b540cf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * 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. @@ -25,6 +25,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; @@ -43,11 +44,17 @@ class RedisAutoConfigurationJedisTests { .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)); @Test - void testDefaultRedisConfiguration() { + void connectionFactoryDefaultsToJedis() { this.contextRunner.run((context) -> assertThat(context.getBean("redisConnectionFactory")) .isInstanceOf(JedisConnectionFactory.class)); } + @Test + void connectionFactoryIsNotCreatedWhenLettuceIsSelected() { + this.contextRunner.withPropertyValues("spring.redis.client-type=lettuce") + .run((context) -> assertThat(context).doesNotHaveBean(RedisConnectionFactory.class)); + } + @Test void testOverrideRedisConfiguration() { this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1").run((context) -> { 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 4b50cbcd9e..7e9a474ab0 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 @@ -37,6 +37,7 @@ import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisSentinelConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; @@ -53,8 +54,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** - * Tests for {@link RedisAutoConfiguration} when both Jedis and Lettuce are on the - * classpath. + * Tests for {@link RedisAutoConfiguration}. * * @author Dave Syer * @author Christian Dupuis @@ -74,10 +74,10 @@ class RedisAutoConfigurationTests { @Test void testDefaultRedisConfiguration() { this.contextRunner.run((context) -> { - assertThat(context.getBean("redisTemplate", RedisOperations.class)).isNotNull(); - assertThat(context.getBean(StringRedisTemplate.class)).isNotNull(); - assertThat(context.getBean("redisConnectionFactory")).isInstanceOf(LettuceConnectionFactory.class); - assertThat(context.getBeanProvider(JedisConnectionConfiguration.class).getIfAvailable()).isNull(); + assertThat(context.getBean("redisTemplate")).isInstanceOf(RedisOperations.class); + assertThat(context).hasSingleBean(StringRedisTemplate.class); + assertThat(context).hasSingleBean(RedisConnectionFactory.class); + assertThat(context.getBean(RedisConnectionFactory.class)).isInstanceOf(LettuceConnectionFactory.class); }); } @@ -187,17 +187,18 @@ class RedisAutoConfigurationTests { } @Test - void testRedisConfigurationWithClientNameJedis() { - this.contextRunner.withPropertyValues("spring.redis.client-type:jedis") - .run((context) -> assertThat(context.getBean("redisConnectionFactory")) - .isInstanceOf(JedisConnectionFactory.class)); + void connectionFactoryWithJedisClientType() { + this.contextRunner.withPropertyValues("spring.redis.client-type:jedis").run((context) -> { + assertThat(context).hasSingleBean(RedisConnectionFactory.class); + assertThat(context.getBean(RedisConnectionFactory.class)).isInstanceOf(JedisConnectionFactory.class); + }); } @Test - void testRedisConfigurationWithClientNameLettuce() { + void connectionFactoryWithLettuceClientType() { this.contextRunner.withPropertyValues("spring.redis.client-type:lettuce").run((context) -> { - assertThat(context.getBean("redisConnectionFactory")).isInstanceOf(LettuceConnectionFactory.class); - assertThat(context.getBeanProvider(JedisConnectionConfiguration.class).getIfAvailable()).isNull(); + assertThat(context).hasSingleBean(RedisConnectionFactory.class); + assertThat(context.getBean(RedisConnectionFactory.class)).isInstanceOf(LettuceConnectionFactory.class); }); }