序列化:
默认使用的hessian序列化中对Map的序列化使用com.alibaba.com.caucho.hessian.io.MapSerializer.
大致逻辑就是除了把map对象的key-value键值对序列化进去,默认还会将class也序列化进去。
@Override
public void writeObject(Object obj, AbstractHessianOutput out)
throws IOException {
if (out.addRef(obj))
return;
Map map = (Map) obj;
Class cl = obj.getClass();
if (cl.equals(HashMap.class)
|| !_isSendJavaType
|| !(obj instanceof java.io.Serializable))
out.writeMapBegin(null);
else
out.writeMapBegin(obj.getClass().getName());
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
out.writeObject(entry.getKey());
out.writeObject(entry.getValue());
}
out.writeMapEnd();
}
反序列化:
默认使用的hessian序列化中对Map的反序列化使用com.alibaba.com.caucho.hessian.io.MapDeserializer.
大致分一下两段逻辑:
构造方法中的逻辑:class类型存在无参构造方法时,直接使用对应的构造方法,否则使用HashMap的无参构造器。
private Class _type;
private Constructor _ctor;
public MapDeserializer(Class type) {
if (type == null)
type = HashMap.class;
_type = type;
Constructor[] ctors = type.getConstructors();
for (int i = 0; i < ctors.length; i++) {
if (ctors[i].getParameterTypes().length == 0)
_ctor = ctors[i];
}
if (_ctor == null) {
try {
_ctor = HashMap.class.getConstructor(new Class[0]);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
反序列化逻辑:类型为null或为Map的,直接只用HashMap。类型为SortedMap的,使用TreeMap,否则使用上一步中构造器去new一个实例。然后将键值对反序列化后put到创建的map实例中
@Override
public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType) throws IOException {
Map map;
if (_type == null)
map = new HashMap();
else if (_type.equals(Map.class))
map = new HashMap();
else if (_type.equals(SortedMap.class))
map = new TreeMap();
else {
try {
map = (Map) _ctor.newInstance();
} catch (Exception e) {
throw new IOExceptionWrapper(e);
}
}
in.addRef(map);
doReadMap(in, map, expectKeyType, expectValueType);
in.readEnd();
return map;
}
总结:
1. 所以在返回类型中使用一些没有无参构造器的类作为成员变量或返回值时,实际创建的对象是HashMap,那么就可能会出现赋值时的类型不匹配的错误。
比如:
@Data
public class MapResponse implements Serializable {
private ImmutableMap immutableMap;
}
/**
* map序列化测试接口
**/
@InnerAPI(path = "/test/map")
Response<MapResponse> testMap();
直接dubbo调用时,就会出现以下这种错误:

- 而上面示例通过泛化调用,则不会有问题。因为不存在赋值的步骤,对象类型在服务端序列化之前经过org.apache.dubbo.rpc.filter.GenericFilter中的onResponse处理过,自定义类型会转成待class键值对的HashMap.
网友评论