自定义SpringCache For Redis配置
2022-06-28 20:24:37 733
SpringCache抽象了缓存使用场景, 对外提供注解, 无需繁琐配置即可获得缓存能力
默认支持一堆缓存中间件, 其中就包括Redis
在此仅提供一种缓存配置的思路
@Data
@Component
@ConfigurationProperties(prefix = "spring.cache.redis")
public class RedisCacheProperties {
/**
* 区分于业务使用的Redis库
*
* 独立SpringCache的数据
*
* 也可以改动配置使用另外的Redis
*/
private Integer database;
}
@Configuration(proxyBeanMethods = false)
public class RedisCacheConfig {
private static final String KEY_SEPARATOR = "::";
//定义缓存的序列化方式
@Bean("objectMapperForRedisSerializer")
public ObjectMapper objectMapperForRedisSerializer(JavaTimeModule javaTimeModule) {
var om = new ObjectMapper();
om.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(null)));
om.registerModule(javaTimeModule);
om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.EVERYTHING,
JsonTypeInfo.As.PROPERTY);
return om;
}
@Bean("redisConnectionFactoryForSpringCache")
public LettuceConnectionFactoryWrapper redisConnectionFactoryForSpringCache(
LettuceConnectionFactory redisConnectionFactory, RedisCacheProperties redisCacheProperties) {
RedisStandaloneConfiguration standaloneConfiguration = redisConnectionFactory.getStandaloneConfiguration();
RedisStandaloneConfiguration standaloneConfigurationNew = new RedisStandaloneConfiguration();
BeanUtil.copyProperties(standaloneConfiguration, standaloneConfigurationNew,
CopyOptions.create().setIgnoreNullValue(true));
standaloneConfigurationNew.setDatabase(redisCacheProperties.getDatabase());
LettuceConnectionFactory lettuceConnectionFactoryNew = new LettuceConnectionFactory(standaloneConfigurationNew);
BeanUtil.copyProperties(redisConnectionFactory, lettuceConnectionFactoryNew, CopyOptions.create()
.setIgnoreNullValue(true).setIgnoreProperties("standaloneConfig", "configuration"));
lettuceConnectionFactoryNew.setDatabase(redisCacheProperties.getDatabase());
lettuceConnectionFactoryNew.afterPropertiesSet();
return LettuceConnectionFactoryWrapper.build(lettuceConnectionFactoryNew);
}
@Bean
public CachingConfigurer cachingConfigurer(CacheProperties cacheProperties,
ObjectProvider<CacheManagerCustomizer<?>> customizers,
ObjectProvider<RedisCacheConfiguration> redisCacheConfiguration,
ObjectProvider<RedisCacheManagerBuilderCustomizer> builderCustomizers,
LettuceConnectionFactoryWrapper redisConnectionFactoryWrapper,
@Qualifier("objectMapperForRedisSerializer") ObjectMapper om) {
RedisCacheManager cacheManager = buildCacheManager(cacheProperties, customizers, redisCacheConfiguration,
builderCustomizers, redisConnectionFactoryWrapper.getLettuceConnectionFactory(), om);
KeyGenerator keyGenerator = buildKeyGenerator();
IgnoreExceptionCacheErrorHandler errorHandler = new IgnoreExceptionCacheErrorHandler();
return new CachingConfigurer() {
@Override
public CacheManager cacheManager() {
return cacheManager;
}
@Override
public KeyGenerator keyGenerator() {
return keyGenerator;
}
@Override
public CacheErrorHandler errorHandler() {
return errorHandler;
}
};
}
/**
* 自定义Redis Key生成策略
*/
public KeyGenerator buildKeyGenerator() {
return (target, method, params) -> {
var builder = new StringBuilder();
for (var param : params) {
builder.append(param).append(KEY_SEPARATOR);
}
builder.append(target.getClass().getName()).append(KEY_SEPARATOR);
builder.append(method.getName());
return builder.toString();
};
}
RedisCacheManager buildCacheManager(CacheProperties cacheProperties,
ObjectProvider<CacheManagerCustomizer<?>> customizers,
ObjectProvider<RedisCacheConfiguration> redisCacheConfiguration,
ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
LettuceConnectionFactory redisConnectionFactory,
ObjectMapper om) {
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(determineConfiguration(cacheProperties, om, redisCacheConfiguration));
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
if (cacheProperties.getRedis().isEnableStatistics()) {
builder.enableStatistics();
}
redisCacheManagerBuilderCustomizers.orderedStream().forEach(customizer -> customizer.customize(builder));
return new CacheManagerCustomizers(customizers.orderedStream().toList()).customize(builder.build());
}
private RedisCacheConfiguration determineConfiguration(
CacheProperties redisCacheProperties,
ObjectMapper om,
ObjectProvider<RedisCacheConfiguration> redisCacheConfiguration) {
return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(redisCacheProperties, om));
}
private RedisCacheConfiguration createConfiguration(CacheProperties cacheProperties, ObjectMapper om) {
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config.serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(om)));
config = config.serializeKeysWith(SerializationPair.fromSerializer(new StringRedisSerializer()));
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
@Slf4j
static class IgnoreExceptionCacheErrorHandler implements CacheErrorHandler {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
log.error("cacheGetError, key: {}", key, exception);
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
log.error("cachePutError, key: {}", key, exception);
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
log.error("cacheEvictError, key: {}", key, exception);
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
log.error("cacheClearError", exception);
}
}
@Data
static class LettuceConnectionFactoryWrapper {
private LettuceConnectionFactory lettuceConnectionFactory;
public static LettuceConnectionFactoryWrapper build(LettuceConnectionFactory lettuceConnectionFactory) {
LettuceConnectionFactoryWrapper wrapper = new LettuceConnectionFactoryWrapper();
wrapper.lettuceConnectionFactory = lettuceConnectionFactory;
return wrapper;
}
}
对应配置文件
spring:
redis:
client-type: lettuce
database: 0
host: 192.168.101.128
port: 6379
password: 123456
cache:
redis:
time-to-live: 10m
database: 1