Kryo 反序列化失败--Class cannot be created (non-static member class)
Kryo 反序列化失败--Class cannot be created (non-static member class)
kryo版本
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.2</version>
</dependency>
log 异常信息
com.esotericsoftware.kryo.KryoException: Class cannot be created (non-static member class): java.util.HashMap$KeySet
Serialization trace:
hitLabels (com.netease.is.ni.api.custom.classify.CustomClassifyResultDTO)
at com.esotericsoftware.kryo.Kryo$DefaultInstantiatorStrategy.newInstantiatorOf(Kryo.java:1308)
at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1127)
at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1136)
at com.esotericsoftware.kryo.serializers.CollectionSerializer.create(CollectionSerializer.java:107)
at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:111)
at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:40)
at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731)
at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:125)
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:543)
at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:709)
at com.netease.is.ni.common.utils.KryoUtils.deserialize(KryoUtils.java:158)
at com.netease.is.ni.common.utils.KryoUtils.deserialize(KryoUtils.java:145)
排查定位
根据log日志定位到newInstantiatorOf类中的1308行代码抛出的异常,进入看一下
if (type.isMemberClass() && !Modifier.isStatic(type.getModifiers()))
throw new KryoException("Class cannot be created (non-static member class): " + className(type));
其中 type是一个class类型,这个if意思是type是否是个成员类,而根据log中的信息显示,用到了HashMap.KeySet()方法生成了一个set集合,那我们手动测试一波看下这个set集合是不是成员类
Map<Object, Object> map = new HashMap<>();
map.put("textCentrality", "0.796");
map.put("userDBMapping", false);
log.info("is member class={}",map.keySet().getClass().isMemberClass());
---------log----------
is member class=true
显然第一个if条件就不符合故报错,那么第二个条件是什么含义呢?
从名字来看,判断这个类的修饰符是否是静态的,如果不是静态的则报错,那么我们再测试一下
Map<Object, Object> map = new HashMap<>();
map.put("textCentrality", "0.796");
map.put("userDBMapping", false);
int modifiers = map.keySet().getClass().getModifiers();//获取修饰符编码
log.info("modifiers={}",modifiers);
log.info("isStatic={}",Modifier.isStatic(modifiers));//判断是否是静态类
------log-----------------
modifiers=16
isStatic=false
显然这个条件也符合,那么在源码中HashMap.KeySet()生成的集合是否真是这样呢?看一眼源码
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
通过源码我们可以得知,最终的set集合是通过EntrySet这个内部类创建而来,并且没有static修饰,跟我们验证的情况一致
解决方法
既然用HashMap.KeySet()方法在反序列化的时候失败,那么使用new HashSet<>(HashMap.KeySet())在包装一层应该可以解决
Map<Object, Object> map = new HashMap<>();
map.put("textCentrality", "0.796");
map.put("userDBMapping", false);
Set<Object> set=new HashSet<>(map.keySet());
int modifiers = set.getClass().getModifiers();
log.info("is member class={}",set.getClass().isMemberClass());
log.info("modifiers={}",modifiers);
log.info("isStatic={}",Modifier.isStatic(modifiers));
------log-------
2023-01-03 20:07:59.272 INFO 18904 --- [nio-8081-exec-4] org.example.Controller : is member class=false
2023-01-03 20:07:59.273 INFO 18904 --- [nio-8081-exec-4] org.example.Controller : modifiers=1
2023-01-03 20:07:59.273 INFO 18904 --- [nio-8081-exec-4] org.example.Controller : isStatic=false
首先new HashSet出来的就不是一个成员类,因此可以解决此异常

浙公网安备 33010602011771号