使用 Spring Boot 2.1,我使用 Java 配置在配置文件中定义 RedisCacheManager bean。一切正常,但我有时想禁用它,例如在测试中。 Spring Boot 提供了
spring.cache.type=NONE
来禁用缓存,根据此 documentation。然而,这个属性将不起作用,因为我已经定义了一个 CacheManager,因此 Spring Boot 不会配置我想要的 NoOpCacheManager(@ConditionalOnMissingBean(CacheManager.class)
上有一个 NoOpCacheConfiguration
,其优先级低于 RedisCacheConfiguration
)。
在定义缓存时,无论提供者是什么(例如 Caffeine),我们通常将它们定义为 beans,然后由 Spring Boot 的自动配置解析为
SimpleCacheManager
。
比如说
@Bean
public Cache myCache() {
return new CaffeineCache(
"my-cache",
Caffeine.newBuilder()
.maximumSize(10)
.build());
}
不幸的是,这对于 Redis 来说是不可能的,因为它的
Cache
实现 RedisCache
不是公开的。
我们喜欢做的另一件事是定义一个 bean
CacheManagerCustomizer<?>
,例如使用 Caffeine
@Bean
public CacheManagerCustomizer<CaffeineCacheManager> caffeineCacheManager() {
return cacheManager -> cacheManager
.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES));
}
这对于 Redis 来说是不可能的,因为
RedisCacheManager
是不可变的。
所以现在唯一的解决方案是创建我们自己的RedisCacheManager,但这会阻止使用
spring.cache.type: NONE
。
这是我的问题。使用 Spring Boot 配置 Redis 缓存以便我们可以根据需要禁用它的最佳方法是什么?
我需要从
spring.cache.type
属性启用/禁用 Redis 自动配置。下面的代码解决了我的问题。这可能对那些想通过更改单个属性来禁用/启用 Redis 的人有所帮助,在我的例子中是 spring.cache.type=redis
。以下是主要配置。
@SpringBootApplication(exclude = {RedisAutoConfiguration.class})
public class SpringBootApp extends SpringBootServletInitializer {
}
@ConditionalOnProperty(prefix = "spring", name = "cache.type", havingValue = "redis")
@Configuration
@Import({ RedisAutoConfiguration.class })
public class ApplicationRedisConfig {
}
要从
application.yaml
启用自动配置
spring:
cache:
type: redis
redis.host: redis
当 redis 不可用时,健康检查会给出以下响应,这表明自动配置已包含在内。
{
"status": "DOWN",
"details": {
"diskSpace": {
"status": "UP",
"details": {
"total": 486730272768,
"free": 216405499904,
"threshold": 10485760
}
},
"db": {
"status": "UP",
"details": {
"database": "PostgreSQL",
"hello": 1
}
},
"elasticsearch": {
"status": "UP"
},
"redis": {
"status": "DOWN",
"details": {
"error": "org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to redis:6379"
}
}
}
}
要禁用自动配置
application.yaml
spring:
cache:
type: simple
redis.host: redis
健康检查给出以下响应,表明 redis 已被排除在自动配置之外。
{
"status": "UP",
"details": {
"diskSpace": {
"status": "UP",
"details": {
"total": 486730272768,
"free": 215928782848,
"threshold": 10485760
}
},
"db": {
"status": "UP",
"details": {
"database": "PostgreSQL",
"hello": 1
}
},
"elasticsearch": {
"status": "UP"
}
}
}
排除
@SpringBootApplication
注释的属性,例如:@SpringBootApplication( exclude = { RedisAutoConfiguration.class } )
和 设置:
spring.data.redis.repositories.enabled=false
您可以使用resilience4j-spring-boot2依赖项 org.springframework.cache.annotation.CachingConfigurer。 该解决方案适用于最新的 Spring-boot 版本 6.x。 它对我有用。 以下是代码片段:
Pom.xml:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>2.1.0</version>
</dependency>
缓存配置类:
package com.redis.sample;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CachingConfiguration implements CachingConfigurer {
@Override
public CacheErrorHandler errorHandler() {
return new CustomCacheErrorHandler();
}
}
CustomCacheErrorHandler 类:
package com.redis.sample;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.stereotype.Component;
@Component
public class CustomCacheErrorHandler implements CacheErrorHandler {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
//do something as per usecase
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
//do something as per usecase
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
//do something as per usecase
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
//do something as per usecase
}
}
CircuitBreakerConfig.java 配置类 包 com.redis.sample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
@Configuration
public class CircuitBreakerConfig {
@Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
io.github.resilience4j.circuitbreaker.CircuitBreakerConfig config = io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.custom()
.failureRateThreshold(6)
.permittedNumberOfCallsInHalfOpenState(2)
.slidingWindowSize(5)
.minimumNumberOfCalls(5)
.build();
return CircuitBreakerRegistry.of(config);
}
@Bean
public CircuitBreaker defaultCircuitBreaker() {
io.github.resilience4j.circuitbreaker.CircuitBreakerConfig config = io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.custom()
.minimumNumberOfCalls(2)
.build();
return CircuitBreaker.of("default", config);
}
}