SpringBoot使用@Cacheable出现预览工具乱码的解决方法

 更新时间:2023年10月06日 09:14:38   作者:逆水行舟x  
直接使用注解进行缓存数据,我们再使用工具去预览存储的数据时发现是乱码,这是由于默认序列化的问题,所以接下来将给大家介绍一下SpringBoot使用@Cacheable出现预览工具乱码的解决方法,需要的朋友可以参考下

直接使用注解进行缓存数据,我们再使用工具去预览存储的数据时发现是乱码,这是由于默认序列化的问题,默认是使用JdkSerializationRedisSerializer,我们将其更改即可

解决办法

我们重新定义一个org.springframework.data.redis.cache.RedisCacheConfiguration的Bean,并修改序列化器即可

/**
 * 此类解决 @Cacheable 存入的缓存使用预览工具时乱码问题
 *
 * @author YinShangwen
 * @since 2023/10/5 14:02
 */
@Configuration
public class RedisCacheConfig {
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        // 工具预览乱码问题
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

⚠️
注意:如果之前有@Cacheable方式存储的缓存需要清理掉。否则会因为序列化/反序列化方式不一致而导致错误

源码导读

RedisCache#put

找到 org.springframework.data.redis.cache.RedisCache#put 方法。这个方法就是最终存入的方法

/*
 * (non-Javadoc)
 * @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object)
 */
@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());
}

serializeCacheValue

我们注意到serializeCacheValue(cacheValue)

private final RedisCacheConfiguration cacheConfig;
/**
 * Serialize the value to cache.
 *
 * @param value must not be {@literal null}.
 * @return never {@literal null}.
 */
protected byte[] serializeCacheValue(Object value) {
    if (isAllowNullValues() && value instanceof NullValue) {
        return BINARY_NULL_VALUE;
    }
    return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
}

getValueSerializationPair

再看 cacheConfig.getValueSerializationPair() 是什么

private final SerializationPair<Object> valueSerializationPair;
/**
 * @return never {@literal null}.
 */
public SerializationPair<Object> getValueSerializationPair() {
    return valueSerializationPair;
}

这个变量就是最终决定序列化的类了,如何设置呢?在RedisCacheConfiguration中有如下方法

/**
 * Define the {@link SerializationPair} used for de-/serializing cache values.
 *
 * @param valueSerializationPair must not be {@literal null}.
 * @return new {@link RedisCacheConfiguration}.
 */
public RedisCacheConfiguration serializeValuesWith(SerializationPair<?> valueSerializationPair) {
    Assert.notNull(valueSerializationPair, "ValueSerializationPair must not be null!");
    return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
            valueSerializationPair, conversionService);
}

默认的RedisCacheConfiguration如何被装载

找到类org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration

注意:类名相同但包路径不相同
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.data.redis.cache.RedisCacheConfiguration

class RedisCacheConfiguration {
    ...
	private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(
			CacheProperties cacheProperties, ClassLoader classLoader) {
		Redis redisProperties = cacheProperties.getRedis();
		org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
				.defaultCacheConfig();
        // 重点
		config = config.serializeValuesWith(
				SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
		if (redisProperties.getTimeToLive() != null) {
			config = config.entryTtl(redisProperties.getTimeToLive());
		}
		if (redisProperties.getKeyPrefix() != null) {
			config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
		}
		if (!redisProperties.isCacheNullValues()) {
			config = config.disableCachingNullValues();
		}
		if (!redisProperties.isUseKeyPrefix()) {
			config = config.disableKeyPrefix();
		}
		return config;
	}
}

我们只看org.springframework.data.redis.cache.RedisCacheConfiguration是如何创建的所以省略了部分代码

我们看到默认使用的序列化器是 JdkSerializationRedisSerializer

以上就是SpringBoot使用@Cacheable出现预览工具乱码的解决方法的详细内容,更多关于SpringBoot使用@Cacheable出现乱码的资料请关注脚本之家其它相关文章!

相关文章

  • Mybatis-Plus之ID自动增长的设置实现

    Mybatis-Plus之ID自动增长的设置实现

    本文主要介绍了Mybatis-Plus之ID自动增长的设置实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Springmvc完成ajax功能实例详解

    Springmvc完成ajax功能实例详解

    在本篇文章里小编给大家整理了关于Springmvc完成ajax功能实例内容,有需要的朋友们可以参考学习下。
    2019-09-09
  • MyBatisPlus的简介及案例详解

    MyBatisPlus的简介及案例详解

    MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率。本文将为大家详细介绍一下MyBatisPlus是使用,需要的可以参考一下
    2022-07-07
  • java实现队列数据结构代码详解

    java实现队列数据结构代码详解

    这篇文章主要介绍了java实现队列数据结构代码详解,简单介绍了队列结构以应用场景,涉及详细实现代码,还是比较不错的,这里分享给大家,需要的朋友可以参考下。
    2017-11-11
  • java算法入门之有效的括号删除有序数组中的重复项实现strStr

    java算法入门之有效的括号删除有序数组中的重复项实现strStr

    大家好,我是哪吒,一个热爱编码的Java工程师,本着"欲速则不达,欲达则欲速"的学习态度,在程序猿这条不归路上不断成长,所谓成长,不过是用时间慢慢擦亮你的眼睛,少时看重的,年长后却视若鸿毛,少时看轻的,年长后却视若泰山,成长之路,亦是渐渐放下执念,内心归于平静的旅程
    2021-08-08
  • SpringMVC访问静态资源的三种方式小结

    SpringMVC访问静态资源的三种方式小结

    这篇文章主要介绍了SpringMVC访问静态资源的三种方式小结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • JAVA获得域名IP地址的方法

    JAVA获得域名IP地址的方法

    这篇文章主要介绍了JAVA获得域名IP地址的方法,涉及java域名操作的相关技巧,需要的朋友可以参考下
    2015-06-06
  • SpringAOP中@annotation与execution的深度示例对比分析

    SpringAOP中@annotation与execution的深度示例对比分析

    Spring AOP中@annotation通过注解标记方法,适合定制化横切逻辑;execution基于方法签名匹配,用于通用功能增强,两者互补,根据需求选择,本文给大家介绍SpringAOP中@annotation与execution的深度示例对比分析,感兴趣的朋友一起看看吧
    2025-08-08
  • Java函数式编程(十一):遍历目录

    Java函数式编程(十一):遍历目录

    这篇文章主要介绍了Java函数式编程(十一):遍历目录,本文是系列文章的第11篇,其它文章请参阅本文底部的相关文章,需要的朋友可以参考下
    2014-09-09
  • Spring实现IoC和DI的方法详解

    Spring实现IoC和DI的方法详解

    IoC全称Inversion of Control (控制反转) ,这里的控制其实是控制权的意思,可以理解为对象的获取权力和方式发生了发转,DI依赖注⼊是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象,本文介绍了Spring实现IoC和DI的方法
    2024-08-08

最新评论