springboot集成redis序列化问题汇总

    1.redis操作方式一:RedisTemplate
    2.redis操作方式二:基于Spring Cache
    spring中使用redis目前接触的到的有两种方式,一种是使用封装的RedisTemplate工具类;另一种是使用Spring Cache,cache实现方式选择RedisCache.两种方案中都需要对序列化方式进行设置,否则会出现二进制格式的数据(不论是Redis desktop manager工具还是redis自带客户端黑窗口打开),如果需要查看数据则会影响观感,关于序列化方式设置底层都是对RedisSerializer接口进行设置,现分别说明为何出现此问题以及如何避免.
springboot集成redis序列化问题汇总_第1张图片

1.redis操作方式一:RedisTemplate

    第一种:springboot中常用的redis操作对象:RedisTemplate(StringRedisTemplate属于RedisTemplate子类,此处不做说明)
springboot集成redis序列化问题汇总_第2张图片
    对于RedisTemplate,如果不指定默认的序列化方式,默认为JdkSerializationRedisSerializer,源码分析如下:
    RedisTemplate继承RedisAccessor,RedisAccessor实现InitializingBean接口,RedisTemplate重写afterPropertiesSet,容器加载实例化后进行设置序列化方式属性信息.

springboot集成redis序列化问题汇总_第3张图片

public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {

	// 忽略部分代码
	private @Nullable RedisSerializer<?> defaultSerializer;
	@SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
	@SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
	@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
	@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
	
	@Override
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		boolean defaultUsed = false;
		// 默认序列化方式不设置则使用JdkSerializationRedisSerializer
		if (defaultSerializer == null) {
			defaultSerializer = new JdkSerializationRedisSerializer(
					classLoader != null ? classLoader : this.getClass().getClassLoader());
		}
		if (enableDefaultSerializer) {
		// 存储key的序列化方式不设置则使用默认序列化方式(不设置则为JdkSerializationRedisSerializer)
			if (keySerializer == null) {
				keySerializer = defaultSerializer;
				defaultUsed = true;
			}
			// 存储value的序列化方式不设置则使用默认序列化方式(不设置则为JdkSerializationRedisSerializer)
			if (valueSerializer == null) {
				valueSerializer = defaultSerializer;
				defaultUsed = true;
			}
				// 存储HashKey的序列化方式不设置则使用默认序列化方式(不设置则为JdkSerializationRedisSerializer)
			if (hashKeySerializer == null) {
				hashKeySerializer = defaultSerializer;
				defaultUsed = true;
			}
			// 存储hashValueSerializer的序列化方式不设置则使用默认序列化方式(不设置则为JdkSerializationRedisSerializer)
			if (hashValueSerializer == null) {
				hashValueSerializer = defaultSerializer;
				defaultUsed = true;
			}
		}
		if (enableDefaultSerializer && defaultUsed) {
			Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
		}
		if (scriptExecutor == null) {
			this.scriptExecutor = new DefaultScriptExecutor<>(this);
		}
		initialized = true;
	}
	// 忽略部分代码
	}

    RedisSerializer进行存储数据时采用的序列化方式有多种,实现类如下:
在这里插入图片描述
    最常用的就是json序列化方式:GenericJackson2JsonRedisSerializer或Jackson2JsonRedisSerializer,所以在redis相关配置类型中修改序列化方式即可.
序列化配置修改代码:

  jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);  // 设置缓存存储对象
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);  // 设置缓存存储对象
        redisTemplate.afterPropertiesSet();

    这里贴一下完整redisTemplate配置类,如果有需要可以作为参考:

@Component
public class RedisTemplateConfig {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 自定义的string序列化器和fastjson序列化器
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 过滤未知字段
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        //只序列化必要shiro字段
        String [] needSerialize = {"realmPrincipals"};
        objectMapper.addMixIn(SimplePrincipalCollection.class, IncludeShiroFields.class);
        objectMapper.setFilters(new SimpleFilterProvider().addFilter("shiroFilter", SimpleBeanPropertyFilter.filterOutAllExcept(needSerialize)));
        // 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
      objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);


        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);  // 设置缓存存储对象
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);  // 设置缓存存储对象
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

2.redis操作方式二:基于Spring Cache

    第二种使用redis方式:Spring Cache
    Cache接口支持多种实现,Cache接口实现类如下:
springboot集成redis序列化问题汇总_第4张图片
    本文对RedisCache进行说明,常用的注解有@Cacheable、@CacheEvict、@CachePut,此处仅对序列化方式进行说明.RedisCache使用需要创建RedisCacheManager,其中需要对RedisCacheConfiguration进行配置.RedisCacheConfiguration设置序列化方式需要借助SerializationPair,源码如下:

  public RedisCacheConfiguration serializeValuesWith(SerializationPair<?> valueSerializationPair) {
        Assert.notNull(valueSerializationPair, "ValueSerializationPair must not be null!");
        return new RedisCacheConfiguration(this.ttl, this.cacheNullValues, this.usePrefix, this.keyPrefix, this.keySerializationPair, valueSerializationPair, this.conversionService);
    }
 

    SerializationPair设置序列化方式源码如下:

  public interface SerializationPair<T> {
        static <T> RedisSerializationContext.SerializationPair<T> fromSerializer(RedisSerializer<T> serializer) {
            Assert.notNull(serializer, "RedisSerializer must not be null!");
            return new RedisSerializerToSerializationPairAdapter(serializer);
        }

    从Redistemplate中序列化源码分析可知,RedisSerializer实现类序列化方式使用的是默认jdk.
序列化修改配置:

GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
    redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
            RedisSerializationContext
                    .SerializationPair
                    .fromSerializer(genericJackson2JsonRedisSerializer)
    ).entryTtl(Duration.ofSeconds(seconds));

    这里贴一下RedisCacheConfig完整配置,如有需要可以作为参考:

@EnableCaching
@Configuration
public class RedisCacheConfig  {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

    	RedisCacheManager redisCacheManager = new RedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                // 默认策略,未配置的 key 会使用这个
                this.getRedisCacheConfigurationWithTtl(60),
                // 指定 key 策略
                this.getRedisCacheConfigurationMap()
            );
    	redisCacheManager.setTransactionAware(true);
            return redisCacheManager;
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>(16);
        return redisCacheConfigurationMap;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {

   	// 指定使用GenericJackson2JsonRedisSerializer序列化方式
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext
                        .SerializationPair
                        .fromSerializer(genericJackson2JsonRedisSerializer)
        ).entryTtl(Duration.ofSeconds(seconds));

        return redisCacheConfiguration;
    }

     以上为自己总结梳理,如有异议,欢迎评论区评论交流!

你可能感兴趣的:(redis,序列化)