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出来的就不是一个成员类,因此可以解决此异常

posted @ 2023-01-03 20:09  Dream可乐  阅读(348)  评论(0)    收藏  举报