SpringBoot 2.x使用缓存注解时,自定义RedisTemplate序列化对象为json无效的原因,及解决办法

首先我们要知道,当使用缓存注解时,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可根据自己的需求进行配置。

你可能感兴趣的:(SpringBoot,Java,Spring,Redis)