SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析

说明:SpringBoot版本为2.1.6.RELEASE

看了许多同学的博客都是通过自定义RedisCacheManager组件的方式来解决,我这里换一种方式,采用自定义org.springframework.data.redis.cache.RedisCacheConfiguration组件的方式来解决,并附上源码分析过程

首先要明确SpringBoot Data在整合Redis作为Cache的实现方式后,组件之间的依赖关系变为:

CacheAutoConfiguration-->RedisCacheConfiguration(autoconfigure.cache)-->RedisCacheManager-->RedisCache-->RedisCacheConfiguration(redis.cache)

为什么使用@Cacheable注解后,保存的Value在Redis中是乱码,首先想到的是从RedisCache类中的put()方法入手。

put方法的逻辑如下图:

SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析_第1张图片

进入createAndConvertCacheKey(key)、serializeCacheValue(cacheValue)方法中,这里不贴代码了,可以知道对key和value的序列化规则都是从当前类的成员变量cacheConfig中获取配置的,而此成员变量cacheConfig对应的类型是org.springframework.data.redis.cache.RedisCacheConfiguration类:

SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析_第2张图片

进入org.springframework.data.redis.cache.RedisCacheConfiguration类中:

SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析_第3张图片

发现该类类头没有注解,推测它并不是容器中的一个组件,且对两个成员变量keySerializationPairvalueSerializationPair并没有赋予初始值,且唯一的构造器还是private私有的。

但此类中有两个static静态方法对keySerializationPairvalueSerializationPair成员变量进行了赋值,且返回一个实例对象:

SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析_第4张图片

分析到这里,我们就要知道是谁调用了此静态方法:

Ctrl+单击此defaultCacheConfig静态方法,发现是org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration配置类在向IOC容器中注入组件RedisCacheManager时,调用了此静态方法,见下图:

SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析_第5张图片

上图说明:在调用RedisCacheManager.builder方法创建RedisCacheManagerBuilder对象后,调用determineConfiguration方法进行装配RedisCacheConfiguration时调用的此defaultCacheConfig静态方法。

而在determineConfiguration方法中,调用此静态方法之前的前提条件是:this.redisCacheConfiguration不为空。否则就按照后面的逻辑对value实行Java序列化规则:JdkSerializationRedisSerializer

现在就分析当前类的成员变量this.redisCacheConfiguration的赋值逻辑,如下图:

SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析_第6张图片

发现在当前类的有参构造器中,它会从IOC容器中获取org.springframework.data.redis.cache.RedisCacheConfiguration组件并赋值给此成员变量。只不过IOC容器中并没有该可用组件,可以打断点看到:

SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析_第7张图片

解决办法:

通过以上的分析,那我们就可以自己手动向IOC容器中注册一个类型为org.springframework.data.redis.cache.RedisCacheConfiguration的组件,并参照determineConfiguration方法:调用serializeValuesWith方法对value的序列化规则改为json类型

/**
     * 注册自定义RedisCacheConfiguration组件,解决@Cacheable @Cacheput注解在向Redis中保存的Value是java序列化乱码的问题
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config=config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
        return config;
    }

再次打断点观察RedisCacheManager组件的注册逻辑:就可以看到this.redisCacheConfiguration不为null,且对Value的序列化规则已经改成我们自定义的了。

SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析_第8张图片

原创不易,转载请指明出处,谢谢!

你可能感兴趣的:(JAVA)