问题描述
在Spring
项目中使用了@Cacheable
注解并且将缓存放入redis,当从Redis
读取缓存时提示了反序列化异常,无法构造UnmodifiableMap
,没有默认的构造函数
Could not read JSON: Cannot construct instance of `org.apache.commons.collections4.map.UnmodifiableMap` (no Creators, like default constructor, exist): no default constructor found
排查过程
-
关闭
@Cacheable
注解,发现代码能够正常运行,所以代码没有问题,问题在于Redis
-
搭建本地
Spring
项目并还原场景,测试问题是否在于注解导致的缓存对应类的构造函数冲突:@Builder
@NoArgsConstructor
@AllArgsConstructor
, 发现与注解无关,排除 -
由于报错信息在于序列化问题,所以分别去掉
implements Serializable
和serialVersionUID
, 发现去掉序列化实现会报错, 而去掉serialVersionUID
代码仍然可以运行,后得知是JVM
会自动计算生成 -
在排查序列化问题时,发现虽然两个项目都是序列化异常,但是报错信息却不一样,点开本地项目报错的
stackTrace
最后一条是JdkSerializationRedisSerializer.deserialize
,而原项目却是
GenericJackson2JsonRedisSerializer.deserialize
,由此发现两个项目序列化器不同 -
在本地项目中自定义
cacheManager
,并使用GenericJackson2JsonRedisSerializer
作为序列化器,运行后果不其然报了一样的错误,所以问题在于序列化器 -
学习了解
GenericJackson2JsonRedisSerializer
的工作过程,大致为以下几步:- 读取
JSON
数据 - 解析
JSON
数据 - 构造对象实例
- 处理类型信息
- 返回反序列化的对象
- 读取
-
到此才理解为什么报错信息说没有默认构造函数,是因为在反序列化时会构造对象实例,而
UnmodifiableMap
是没有默认构造函数的
结论
Redis
序列化器GenericJackson2JsonRedisSerializer
读取数据时需要实例化对象,UnmodifiableMap
没有构造函数,所以会报错。
解决办法
使用其他构造器,例如默认的JdkSerializationRedisSerializer