昨天在写代码的时候碰到了一个问题,在使用@Cacheable注解实现redis缓存时,第一次调用方法可以正常获取数据,这时将缓存数据存入redis中,一切正常。但是在第二次调用这个方法的时候,也就是会从redis中取数据时程序报错,一个类型转换错误
java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to 'xxx'
这里让我很迷惑,明明是在第一次调用的时候存入redis的数据,为什么在取出来的时候会有类型转换错误?在网上搜集相关报错内容的时候大部分都是使用redisTemplate的opsForValue方法进行存写的时候出现类似问题,并没有看到和我一样的报错情况。
在我的redisConfig中也配置了CacheManager对应的序列化方式,使用的序列化器为FastJsonRedisSerializer
,后来在一篇帖子中找到了解决方式 参考链接:[CSDN]-springboot2.x使用redis作为缓存(使用fastjson序列化的方式,并调试反序列化异常)
根据文章内容,需要使用自定义的FastJsonRedisSerializer
(名称无所谓在配置类中使用自己定义的serializer就好)利用fastjson完成序列化和反序列化,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.serializer.SerializerFeature;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.SerializationException;import java.nio.charset.Charset;public class FastJsonRedisSerializer <T> implements RedisSerializer <T> { private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8" ); private Class<T> clazz; public FastJsonRedisSerializer (Class<T> clazz) { super (); this .clazz = clazz; } @Override public byte [] serialize(T t) throws SerializationException { if (null == t) { return new byte [0 ]; } return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); } @Override public T deserialize (byte [] bytes) throws SerializationException { if (null == bytes || bytes.length <= 0 ) { return null ; } String str = new String (bytes, DEFAULT_CHARSET); return (T) JSON.parseObject(str, clazz); } }
然后在配置类redisConfig
中定义使用自定义的序列化器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Bean @Primary public CacheManager cacheManager (RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer (); FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer <Object>(Object.class); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); ParserConfig.getGlobalInstance().addAccept("com.yahoo.common.utils.R" ); ParserConfig.getGlobalInstance().addAccept("com.baomidou.mybatisplus.extension.plugins.pagination.Page" ); ParserConfig.getGlobalInstance().addAccept("com.yahoo.clothes.entity.Clothes" ); ParserConfig.getGlobalInstance().addAccept("com.yahoo.user.entity.User" ); return cacheManager; }
需要注意的是ParserConfig.getGlobalInstance().addAccept("类名或包")
这里需要设置白名单,否则会报错com.alibaba.fastjson.JSONException: autoType is not support
,比如我这里使用的是自定义的泛型返回类R<T>,需要将泛型T的类也放入白名单,比如我上边写到的clothes或者user类,或者直接将一个包下的所有类都添加白名单ParserConfig.getGlobalInstance().addAccept("com.yahoo.user.entity.")
有关autoType的具体解释参考:[CSDN]-解决com.alibaba.fastjson.JSONException: autoType is not support