问题描述:
以下代码为问题代码:
public class ItemDO implements Serializable {
private static final long serialVersionUID=-463144769925355007L;
...
private Map<String,String> langAndTitleMap;
...
}
public class ItemMultiLangDecorator implements ItemDecorator {
...
@Override
public ItemDO getItemDO() throws IcException {
...
if(titleMultiLangFieldMeta!=null){
itemDO.setLangAndTitleMap(new HashMap<String, String>(){{put(titleMultiLangFieldMeta.getFieldLang(),itemDO.getTitle());}});
}else{
itemDO.setLangAndTitleMap(new HashMap<String, String>(){{put(LanguageConstants.EN,itemDO.getTitle());}});
}
...
}
...
}
在应用发布之后,日志出现ItemDO类序列化报错异常,如下:
java.io.NotSerializableException: com.taobao.item.manager.decorator.ItemMultiLangDecorator
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.taobao.item.util.CloneUtil.deepCloneBySerialize(CloneUtil.java:29)
at com.taobao.item.domain.ItemUpdateDO.mergeDbItem(ItemUpdateDO.java:320)
问题原因
虽然序列化的是ItemDO类,但是报错却是ItemMultiLangDecorator类,表面上看ItemDO里面并无任何对ItemMultiLangDecorator引用,但是由于在构造HashMap实例时候采用了匿名内部类构造的方式,即{{ ... }}
形式,查阅资料得知道java会在匿名内部类的实例都持有一个外部封装类实例的隐式引用,也就导致构建Map实例拥有所在外部类ItemMultiLangDecorator实例引用,而恰好外部类并没有实现序列化接口。
本人也在本地写了一个Demo再次重现该问题。
public class Inner implements Serializable{
private static final long serialVersionUID= -463144769925355007L;
private String name;
public void setName(String name) {
this.name = name;
}
}
public class Outer {
public Inner create(){
return new Inner(){
{
setName("Hello");
}
};
}
}
public class Main {
public static void main(String[] args) {
Inner inner=new Outer().create();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(baos);
oos.writeObject(inner);
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行后会抛出Outer类序列化错误,在IDE中断点,可以看到Inner类持有外部类实例引用,如图: