From 283c9de28fd6cbde5e7e7e83631f1e4327bd4453 Mon Sep 17 00:00:00 2001 From: Ryon Date: Mon, 6 Nov 2017 13:51:03 -0600 Subject: [PATCH 1/2] Improve spring-boot-autoconfigure for Redis caches Expose key prefix, TTL and null value settings for spring-data-redis' RedisCacheConfiguration in Spring .properties/yml configuration files. Example: spring.cache.redis.ttl=PT15M spring.cache.redis.keyPrefix=foo spring.cache.redis.useKeyPrefix=false spring.cache.redis.cacheNullValues=false Fixes gs-10795 --- .../autoconfigure/cache/CacheProperties.java | 77 +++++++++++++++++++ .../cache/RedisCacheConfiguration.java | 29 ++++++- .../cache/CacheAutoConfigurationTests.java | 31 ++++++-- 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheProperties.java index 1d299e70e6..6fe5a6d0b0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheProperties.java @@ -16,8 +16,10 @@ package org.springframework.boot.autoconfigure.cache; +import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -29,6 +31,7 @@ import org.springframework.util.Assert; * * @author Stephane Nicoll * @author Eddú Meléndez + * @author Ryon Day * @since 1.3.0 */ @ConfigurationProperties(prefix = "spring.cache") @@ -55,6 +58,8 @@ public class CacheProperties { private final JCache jcache = new JCache(); + private final Redis redis = new Redis(); + public CacheType getType() { return this.type; } @@ -91,6 +96,10 @@ public class CacheProperties { return this.jcache; } + public Redis getRedis() { + return this.redis; + } + /** * Resolve the config location if set. * @param config the config resource @@ -233,4 +242,72 @@ public class CacheProperties { } + /** + * Redis-specific cache properties. Properties set will be used as the defaults for + * all Redis caches. + * + * @see org.springframework.data.redis.cache.RedisCacheConfiguration + */ + public static class Redis { + + /** + * The TTL for entries. By default, entries do not expire, a value of zero + * indicates an eternal entry, and the given value will be converted to seconds.. + * + * @see Duration#parse(CharSequence) + */ + private Duration ttl = Duration.ZERO; + + /** + * Whether to allow caching of {@literal null} values. + */ + private boolean cacheNullValues = true; + + /** + * The non-null Redis key prefix to use instead of the default one. + */ + private String keyPrefix; + + /** + * Whether to use the key prefix when writing to Redis. + */ + private boolean useKeyPrefix = true; + + public Duration getTtl() { + return this.ttl; + } + + public Redis setTtl(Duration ttl) { + this.ttl = ttl; + return this; + } + + public boolean isCacheNullValues() { + return this.cacheNullValues; + } + + public Redis setCacheNullValues(boolean cacheNullValues) { + this.cacheNullValues = cacheNullValues; + return this; + } + + public Optional getKeyPrefix() { + return Optional.ofNullable(this.keyPrefix); + } + + public Redis setKeyPrefix(String keyPrefix) { + this.keyPrefix = keyPrefix; + return this; + } + + public boolean isUseKeyPrefix() { + return this.useKeyPrefix; + } + + public Redis setUseKeyPrefix(boolean useKeyPrefix) { + this.useKeyPrefix = useKeyPrefix; + return this; + } + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java index ce6e0e2587..d8fa90b192 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java @@ -20,6 +20,7 @@ import java.util.LinkedHashSet; import java.util.List; import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.cache.CacheProperties.Redis; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; @@ -36,6 +37,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; * * @author Stephane Nicoll * @author Mark Paluch + * @author Ryon Day * @since 1.3.0 */ @Configuration @@ -57,13 +59,38 @@ class RedisCacheConfiguration { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { + RedisCacheManagerBuilder builder = RedisCacheManager - .builder(redisConnectionFactory); + .builder(redisConnectionFactory).cacheDefaults(getConfiguration()); + List cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet<>(cacheNames)); } + return this.customizerInvoker.customize(builder.build()); } + private org.springframework.data.redis.cache.RedisCacheConfiguration getConfiguration() { + + Redis redisProperties = this.cacheProperties.getRedis(); + + org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration + .defaultCacheConfig().entryTtl(redisProperties.getTtl()); + + if (redisProperties.getKeyPrefix().isPresent()) { + config = config.prefixKeysWith(redisProperties.getKeyPrefix().get()); + } + + if (!redisProperties.isCacheNullValues()) { + config = config.disableCachingNullValues(); + } + + if (!redisProperties.isUseKeyPrefix()) { + config = config.disableKeyPrefix(); + } + + return config; + } + } 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 9019e77c24..f3342fad5c 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 @@ -90,6 +90,7 @@ import static org.mockito.Mockito.verify; * @author Stephane Nicoll * @author Eddú Meléndez * @author Mark Paluch + * @author Ryon Day */ @RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("hazelcast-client-*.jar") @@ -280,14 +281,25 @@ public class CacheAutoConfigurationTests { @Test public void redisCacheExplicit() { this.contextRunner.withUserConfiguration(RedisCacheConfiguration.class) - .withPropertyValues("spring.cache.type=redis").run((context) -> { + .withPropertyValues("spring.cache.type=redis", + "spring.cache.redis.ttl=PT15M", + "spring.cache.redis.keyPrefix=foo", + "spring.cache.redis.useKeyPrefix=false", + "spring.cache.redis.cacheNullValues=false") + .run((context) -> { RedisCacheManager cacheManager = getCacheManager(context, RedisCacheManager.class); assertThat(cacheManager.getCacheNames()).isEmpty(); - assertThat( - ((org.springframework.data.redis.cache.RedisCacheConfiguration) new DirectFieldAccessor( - cacheManager).getPropertyValue("defaultCacheConfig")) - .usePrefix()).isTrue(); + + org.springframework.data.redis.cache.RedisCacheConfiguration redisCacheConfiguration = (org.springframework.data.redis.cache.RedisCacheConfiguration) new DirectFieldAccessor( + cacheManager).getPropertyValue("defaultCacheConfig"); + + assertThat(redisCacheConfiguration.usePrefix()).isFalse(); + assertThat(redisCacheConfiguration.getKeyPrefix()).contains("foo"); + assertThat(redisCacheConfiguration.getTtl()) + .isEqualTo(java.time.Duration.ofMinutes(15)); + assertThat(redisCacheConfiguration.getAllowCacheNullValues()) + .isFalse(); }); } @@ -309,6 +321,15 @@ public class CacheAutoConfigurationTests { RedisCacheManager cacheManager = getCacheManager(context, RedisCacheManager.class); assertThat(cacheManager.getCacheNames()).containsOnly("foo", "bar"); + + org.springframework.data.redis.cache.RedisCacheConfiguration redisCacheConfiguration = (org.springframework.data.redis.cache.RedisCacheConfiguration) new DirectFieldAccessor( + cacheManager).getPropertyValue("defaultCacheConfig"); + assertThat(redisCacheConfiguration.usePrefix()).isTrue(); + assertThat(redisCacheConfiguration.getKeyPrefix()).isEmpty(); + assertThat(redisCacheConfiguration.getTtl()) + .isEqualTo(java.time.Duration.ofMinutes(0)); + assertThat(redisCacheConfiguration.getAllowCacheNullValues()) + .isTrue(); }); } From 1ba646965c3b6cafe6ab5c969064ac0025ac6bc6 Mon Sep 17 00:00:00 2001 From: Ryon Date: Tue, 7 Nov 2017 10:32:28 -0600 Subject: [PATCH 2/2] Respond to MR feedback * Remove unnecessary formatting changes * Discontinue use of Optional for Redis property configuration * Remove @see tags * Change verbiage for property descriptions Fixes gs-10795 --- .../autoconfigure/cache/CacheProperties.java | 16 ++++++---------- .../cache/RedisCacheConfiguration.java | 7 ++----- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheProperties.java index 6fe5a6d0b0..a0b87e3c1e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheProperties.java @@ -19,7 +19,6 @@ package org.springframework.boot.autoconfigure.cache; import java.time.Duration; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.concurrent.TimeUnit; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -245,16 +244,12 @@ public class CacheProperties { /** * Redis-specific cache properties. Properties set will be used as the defaults for * all Redis caches. - * - * @see org.springframework.data.redis.cache.RedisCacheConfiguration */ public static class Redis { /** - * The TTL for entries. By default, entries do not expire, a value of zero - * indicates an eternal entry, and the given value will be converted to seconds.. - * - * @see Duration#parse(CharSequence) + * Specifies the TTL (ultimately converted to seconds) for keys written to Redis. + * By default, entries do not expire, and a value of {@link Duration#ZERO} disables the TTL. */ private Duration ttl = Duration.ZERO; @@ -264,7 +259,8 @@ public class CacheProperties { private boolean cacheNullValues = true; /** - * The non-null Redis key prefix to use instead of the default one. + * Specifies an override for the default Redis key prefix. A value of {@literal null} results + * in usage of the default key prefix. */ private String keyPrefix; @@ -291,8 +287,8 @@ public class CacheProperties { return this; } - public Optional getKeyPrefix() { - return Optional.ofNullable(this.keyPrefix); + public String getKeyPrefix() { + return this.keyPrefix; } public Redis setKeyPrefix(String keyPrefix) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java index d8fa90b192..057a94313b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java @@ -59,15 +59,12 @@ class RedisCacheConfiguration { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { - RedisCacheManagerBuilder builder = RedisCacheManager .builder(redisConnectionFactory).cacheDefaults(getConfiguration()); - List cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet<>(cacheNames)); } - return this.customizerInvoker.customize(builder.build()); } @@ -78,8 +75,8 @@ class RedisCacheConfiguration { org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration .defaultCacheConfig().entryTtl(redisProperties.getTtl()); - if (redisProperties.getKeyPrefix().isPresent()) { - config = config.prefixKeysWith(redisProperties.getKeyPrefix().get()); + if (redisProperties.getKeyPrefix() != null) { + config = config.prefixKeysWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) {