使用List.of()、Map.of()、Set.of() - Jackson无法反序列化Redis缓存(缓存有类型标识的时候)
说明
List.of()
、Map.of()
、Set.of()
这三者都是从不可变集合的容器类ImmutableCollections
衍生出来的。
如果Redis缓存序列化配置了携带类型一起存入Redis的话:
om.activateDefaultTyping(om.getPolymorphicTypeValidator(), DefaultTyping.NON_FINAL);
使用了List.of()
、Map.of()
、Set.of()
等ImmutableCollections
衍生出来类将无法反序列化,Jackson会报错:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_ARRAY), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.lang.Object)
举个例子
测试
比如,我有一个树节点实体类,并且我Redis缓存序列化和反序列化配置了om.activateDefaultTyping(om.getPolymorphicTypeValidator(), DefaultTyping.NON_FINAL);
:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
public class SimpleTree {
private Long id;
private Long parentId;
private String label;
private List<SimpleTree> children;
}
假设有这么一些数据:
SimpleTree rootTreeDto = new SimpleTree();
rootTreeDto.setId(SysConst.ROOT_MENU_ID);
rootTreeDto.setLabel(SysConst.ROOT_MENU_NAME);
rootTreeDto.setChildren(treeDtoList);
List<SimpleTree> result = List.of(rootTreeDto);
// 测试放入缓存,放入缓存时没问题的
redisTemplate.opsForValue().set("simple_tree", result);
// 测试从缓存取出,报将会报错
Object simpleTreeObj = redisTemplate.opsForValue().get("simple_tree");
当从Redis中反序列化出数据的时候,会出现异常:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_ARRAY), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.lang.Object)
ImmutableCollections
相关源代码
那么就来看一下List.of(xxx)
的源代码:
static <E> List<E> of(E e1) {
return new ImmutableCollections.List12<>(e1);
}
ImmutableCollections
源代码:
/**
* Container class for immutable collections. Not part of the public API.
* Mainly for namespace management and shared infrastructure.
*
* Serial warnings are suppressed throughout because all implementation
* classes use a serial proxy and thus have no need to declare serialVersionUID.
*/
@SuppressWarnings("serial")
class ImmutableCollections {
/**
* A "salt" value used for randomizing iteration order. This is initialized once
* and stays constant for the lifetime of the JVM. It need not be truly random, but
* it needs to vary sufficiently from one run to the next so that iteration order
* will vary between JVM runs.
*/
static final int SALT;
static {
long nt = System.nanoTime();
SALT = (int)((nt >>> 32) ^ nt);
}
/** No instances. */
private ImmutableCollections() { }
// 具体代码略...
}
ImmutableCollections.List12
源代码:
static final class List12<E> extends AbstractImmutableList<E>
implements Serializable {
// 具体代码略...
}
ImmutableCollections
解析
ImmutableCollections
的无参构造方法是私有的,且final类型修饰的(说明它不能被继承),final修饰在这不是重点,重点是无参构造方法是私有的。
我们都知道Jackson反序列化需要一个无参构造方法,或者手动提供一个构造方法,然后使用注解@JsonCreator
修饰,并且在构造方法的入参用@JsonProperty
指明字段名称修饰。
简而言之,一句话:**ImmutableCollections
是无法被反序列化的**!
这就是上面从Redis缓存中使用Jackson反序列化数据处理会报错的原因。
解决办法?
1.使用new ArrayList
new ArrayList(rootTreeDto)
2.使用guava提供的集合工具
Lists.newArrayList(rootTreeDto)
3.使用其它可被反序列化的集合、工具类或自定义反序列化
太多了办法了,略…
4.如果确实想反序列化这几个特殊类
自定义UnmodifiableListDeserializer
、UnmodifiableMapDeserializer
、UnmodifiableSetDeserializer
实现JsonDeserializer
类去达到特殊的结构去自定义反序列化。
也可以看看org.springframework.security.jackson2.CoreJackson2Module
类。
参考文章:
- 本文标签: Java SpringBoot Redis
- 本文链接: http://www.lzhpo.com/article/180
- 版权声明: 本文由lzhpo原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权