首先我们要知道,当使用缓存注解时,RedisCacheManager
帮我们创建RedisCache
来作为缓存组件,RedisCache通过操作redis缓存数据。而在springboot 1.5.x,RedisCache又是通过RedisTemplate来操作redis缓存数据。而在srpingboot 2.x,RedisCache没有使用到RedisTemplate。
下面来看srpingboot 2.x中RedisCache是如何序列化对象的,直接看RedisCache的put方法。
public class RedisCache extends AbstractValueAdaptingCache {
private final RedisCacheConfiguration cacheConfig;
@Override
public void put(Object key, @Nullable Object value) {
Object cacheValue = preProcessCacheValue(value);
if (!isAllowNullValues() && cacheValue == null) {
throw new IllegalArgumentException(String.format(
"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
name));
}
cacheWriter.put(name, createAndConvertCacheKey(key),
serializeCacheValue(cacheValue), cacheConfig.getTtl());
}
/**
* 序列化value
*/
protected byte[] serializeCacheValue(Object value) {
if (isAllowNullValues() && value instanceof NullValue) {
return BINARY_NULL_VALUE;
}
return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
}
}
在put方法中通过serializeCacheValue方法序列化value,在serializeCacheValue方法中通过从RedisCacheConfiguration获取valueSerializationPair类序列化对象。查看RedisCacheConfiguration的默认配置如下:
public class RedisCacheConfiguration {
public static RedisCacheConfiguration defaultCacheConfig() {
return defaultCacheConfig(null);
}
public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader classLoader) {
DefaultFormattingConversionService conversionService =
new DefaultFormattingConversionService();
registerDefaultConverters(conversionService);
return new RedisCacheConfiguration(Duration.ZERO, true, true,
CacheKeyPrefix.simple(),
SerializationPair.fromSerializer(RedisSerializer.string()),
SerializationPair.fromSerializer(RedisSerializer.java(classLoader)),
conversionService);
}
/**
* @param ttl 缓存持续时间
* @param cacheNullValues 是否允许缓存Null值
* @param usePrefix 是否使用前缀
* @param keyPrefix key的前缀
* @param keySerializationPair key的序列化方式
* @param valueSerializationPair value的序列化方式
* @param conversionService
*/
private RedisCacheConfiguration(Duration ttl,
Boolean cacheNullValues,
Boolean usePrefix,
CacheKeyPrefix keyPrefix,
SerializationPair<String> keySerializationPair,
SerializationPair<?> valueSerializationPair,
ConversionService conversionService) {
this.ttl = ttl;
this.cacheNullValues = cacheNullValues;
this.usePrefix = usePrefix;
this.keyPrefix = keyPrefix;
this.keySerializationPair = keySerializationPair;
this.valueSerializationPair = (SerializationPair<Object>) valueSerializationPair;
this.conversionService = conversionService;
}
}
第15、16行就是序列化的默认配置,key采用StringRedisSerializer序列化,value采用jdk序列化,所以自定义RedisTemplate是没有效果的,需要配置RedisCacheConfiguration才行。
解决办法
自定义RedisCacheManager,配置RedisCacheConfiguration的序列化方式为json
@Configuration
public class MyRedisConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 配置序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
RedisCacheConfiguration可根据自己的需求进行配置。