项目场景:
springboot2.x集成了redis时,使用@Cacheable注解进行缓存,同时自定义了一个RedisUtils(使用到了自定义的RedisTemplate)方便获取缓存值
问题描述:
单独使用缓存注解时可以正常存取值,单独使用RedisUtils也可以正常存取值,但通过注解存值,RedisUtils获取值时始终为null
原因分析:
有两点原因:
- RedisUtils中的RedisTemplate并不是配置时注入的bean(因为SpringBoot中也提供了RedisTemplate),这样可能会造成拿不到值的情况(不常见)
- RedisTemplate和RedisCacheManager序列化方式不一样,这个问题需要明确@Cacheable和RedisTemplate调用的方式(下面有提到),在配置中如果两个类序列化的方式不一样,那么混用时也就拿不到数据
- @Cacheable是spring cache提供的,实现方式也是通过CacheManager进行缓存的读写
- RedisTemplate则是springboot提供的一个关于读写redis数据的类
解决方案:
针对第一个原因,可以在自定义RedisTemplate时添加一个@Primary注解,这样就相当于告诉spring容器,如果容器中有多个RedisTemplate,这个自定义的RedisTemplate需要优先被取出,例如:
@Bean
@Primary
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 使用GenericToStringSerializer来序列化和反序列化redis的key
GenericToStringSerializer<String> stringGenericToStringSerializer = new GenericToStringSerializer<>(String.class);
redisTemplate.setKeySerializer(stringGenericToStringSerializer);
redisTemplate.setHashKeySerializer(stringGenericToStringSerializer);
redisTemplate.setStringSerializer(stringGenericToStringSerializer);
// 使用FastJsonRedisSerializer来序列化和反序列化redis的值
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
return redisTemplate;
}
针对第二点原因,只要保证RedisTemplate和CacheManager序列化方式一样即可,最保险的写法如下:
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* redisTemplate相关
*/
@Bean("customRedisTemplate")
@Primary
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 使用GenericToStringSerializer来序列化和反序列化redis的key
GenericToStringSerializer<String> stringGenericToStringSerializer = new GenericToStringSerializer<>(String.class);
redisTemplate.setKeySerializer(stringGenericToStringSerializer);
redisTemplate.setHashKeySerializer(stringGenericToStringSerializer);
redisTemplate.setStringSerializer(stringGenericToStringSerializer);
// 使用FastJsonRedisSerializer来序列化和反序列化redis的值
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
return redisTemplate;
}
/**
* 选择redis作为默认缓存工具
*/
@Bean("redisCacheManager")
@Primary
@DependsOn("customRedisTemplate")
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(redisTemplate.getConnectionFactory())
// 设置缓存默认永不过期
.cacheDefaults(
RedisCacheConfiguration.defaultCacheConfig()
// 不缓存null(需要与unless = "#result == null"共同使用)
.disableCachingNullValues()
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getStringSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())))
// 配置同步修改或删除 put/evict
.transactionAware()
.build();
}
}