Dubbo序列化之hessian2
2017-11-30 本文已影响306人
3c69b7c624d9
根据前面的说明可以知道序列化功能依然使用spi,我们来查看一下
package com.alibaba.dubbo.common.serialize;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
/**
* Serialization. (SPI, Singleton, ThreadSafe)
*
* @author ding.lid
* @author william.liangf
*/
@SPI("hessian2")
public interface Serialization {
/**
* get content type id
*
* @return content type id
*/
byte getContentTypeId();
/**
* get content type
*
* @return content type
*/
String getContentType();
/**
* create serializer
* @param url
* @param output
* @return serializer
* @throws IOException
*/
@Adaptive
ObjectOutput serialize(URL url, OutputStream output) throws IOException;
/**
* create deserializer
* @param url
* @param input
* @return deserializer
* @throws IOException
*/
@Adaptive
ObjectInput deserialize(URL url, InputStream input) throws IOException;
}
很明显 默认情况我们使用hession2来作为序列化
dubbo=com.alibaba.dubbo.common.serialize.support.dubbo.DubboSerialization
hessian2=com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization
java=com.alibaba.dubbo.common.serialize.support.java.JavaSerialization
compactedjava=com.alibaba.dubbo.common.serialize.support.java.CompactedJavaSerialization
json=com.alibaba.dubbo.common.serialize.support.json.JsonSerialization
fastjson=com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization
nativejava=com.alibaba.dubbo.common.serialize.support.nativejava.NativeJavaSerialization
关于hession是caucho提供的协议,知名服务器resin也是他家的产品
据dubbo官方研究表名hession2在系统中推荐生产使用。但是我们发现在序列化过程中出现了丢字段的情况。
下面分析一下序列化
首先从正向序列化来看:
Serializer serializer;
serializer = (Serializer) _staticSerializerMap.get(cl);
if (serializer != null)
return serializer;
if (_cachedSerializerMap != null) {
synchronized (_cachedSerializerMap) {
serializer = (Serializer) _cachedSerializerMap.get(cl);
}
if (serializer != null)
return serializer;
}
for (int i = 0;
serializer == null && _factories != null && i < _factories.size();
i++) {
AbstractSerializerFactory factory;
factory = (AbstractSerializerFactory) _factories.get(i);
serializer = factory.getSerializer(cl);
}
if (serializer != null) {
}
else if (JavaSerializer.getWriteReplace(cl) != null)
serializer = new JavaSerializer(cl, _loader);
else if (HessianRemoteObject.class.isAssignableFrom(cl))
serializer = new RemoteSerializer();
// else if (BurlapRemoteObject.class.isAssignableFrom(cl))
// serializer = new RemoteSerializer();
else if (Map.class.isAssignableFrom(cl)) {
if (_mapSerializer == null)
_mapSerializer = new MapSerializer();
serializer = _mapSerializer;
}
else if (Collection.class.isAssignableFrom(cl)) {
if (_collectionSerializer == null) {
_collectionSerializer = new CollectionSerializer();
}
serializer = _collectionSerializer;
}
else if (cl.isArray())
serializer = new ArraySerializer();
else if (Throwable.class.isAssignableFrom(cl))
serializer = new ThrowableSerializer(cl, getClassLoader());
else if (InputStream.class.isAssignableFrom(cl))
serializer = new InputStreamSerializer();
else if (Iterator.class.isAssignableFrom(cl))
serializer = IteratorSerializer.create();
else if (Enumeration.class.isAssignableFrom(cl))
serializer = EnumerationSerializer.create();
else if (Calendar.class.isAssignableFrom(cl))
serializer = CalendarSerializer.create();
else if (Locale.class.isAssignableFrom(cl))
serializer = LocaleSerializer.create();
else if (Enum.class.isAssignableFrom(cl))
serializer = new EnumSerializer(cl);
if (serializer == null)
serializer = getDefaultSerializer(cl);
if (_cachedSerializerMap == null)
_cachedSerializerMap = new HashMap(8);
synchronized (_cachedSerializerMap) {
_cachedSerializerMap.put(cl, serializer);
}
return serializer;
}
很明显当正常的javabean过来将会获得getDefaultSerializer也就是JavaSerializer
public JavaSerializer(Class cl, ClassLoader loader)
{
introspectWriteReplace(cl, loader);
if (_writeReplace != null)
_writeReplace.setAccessible(true);
ArrayList primitiveFields = new ArrayList();
ArrayList compoundFields = new ArrayList();
for (; cl != null; cl = cl.getSuperclass()) {
Field []fields = cl.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (Modifier.isTransient(field.getModifiers())
|| Modifier.isStatic(field.getModifiers()))
continue;
// XXX: could parameterize the handler to only deal with public
field.setAccessible(true);
if (field.getType().isPrimitive()
|| (field.getType().getName().startsWith("java.lang.")
&& ! field.getType().equals(Object.class)))
primitiveFields.add(field);
else
compoundFields.add(field);
}
}
ArrayList fields = new ArrayList();
fields.addAll(primitiveFields);
fields.addAll(compoundFields);
_fields = new Field[fields.size()];
fields.toArray(_fields);
_fieldSerializers = new FieldSerializer[_fields.length];
for (int i = 0; i < _fields.length; i++) {
_fieldSerializers[i] = getFieldSerializer(_fields[i].getType());
}
}
-
首先查找是否包含writeReplace方法
-
将所有的包含超类的字段全部放入arrayList中(去除静态和瞬态),先放入原生类型后放入包装类型
-
根据field类型放入对应的序列化器
以上初始化完成后将javabean的元信息写入流并且进行实例的序列化
分析完序列化步骤我们来看一下反序列化步骤
同样的道理,默认将会返回JavaDeserializer
public JavaDeserializer(Class cl)
{
_type = cl;
_fieldMap = getFieldMap(cl);
_readResolve = getReadResolve(cl);
if (_readResolve != null) {
_readResolve.setAccessible(true);
}
Constructor []constructors = cl.getDeclaredConstructors();
long bestCost = Long.MAX_VALUE;
for (int i = 0; i < constructors.length; i++) {
Class []param = constructors[i].getParameterTypes();
long cost = 0;
for (int j = 0; j < param.length; j++) {
cost = 4 * cost;
if (Object.class.equals(param[j]))
cost += 1;
else if (String.class.equals(param[j]))
cost += 2;
else if (int.class.equals(param[j]))
cost += 3;
else if (long.class.equals(param[j]))
cost += 4;
else if (param[j].isPrimitive())
cost += 5;
else
cost += 6;
}
if (cost < 0 || cost > (1 << 48))
cost = 1 << 48;
cost += (long) param.length << 48;
if (cost < bestCost) {
_constructor = constructors[i];
bestCost = cost;
}
}
if (_constructor != null) {
_constructor.setAccessible(true);
Class []params = _constructor.getParameterTypes();
_constructorArgs = new Object[params.length];
for (int i = 0; i < params.length; i++) {
_constructorArgs[i] = getParamArg(params[i]);
}
}
}
- 获取字段集合
- 获取readResolve方法
- 获取最佳构造函数
获取字段集合代码如下
/**
* Creates a map of the classes fields.
*/
protected HashMap getFieldMap(Class cl)
{
HashMap fieldMap = new HashMap();
for (; cl != null; cl = cl.getSuperclass()) {
Field []fields = cl.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (Modifier.isTransient(field.getModifiers())
|| Modifier.isStatic(field.getModifiers()))
continue;
else if (fieldMap.get(field.getName()) != null)
continue;
// XXX: could parameterize the handler to only deal with public
try {
field.setAccessible(true);
} catch (Throwable e) {
e.printStackTrace();
}
Class type = field.getType();
FieldDeserializer deser;
if (String.class.equals(type))
deser = new StringFieldDeserializer(field);
else if (byte.class.equals(type)) {
deser = new ByteFieldDeserializer(field);
}
else if (short.class.equals(type)) {
deser = new ShortFieldDeserializer(field);
}
else if (int.class.equals(type)) {
deser = new IntFieldDeserializer(field);
}
else if (long.class.equals(type)) {
deser = new LongFieldDeserializer(field);
}
else if (float.class.equals(type)) {
deser = new FloatFieldDeserializer(field);
}
else if (double.class.equals(type)) {
deser = new DoubleFieldDeserializer(field);
}
else if (boolean.class.equals(type)) {
deser = new BooleanFieldDeserializer(field);
}
else if (java.sql.Date.class.equals(type)) {
deser = new SqlDateFieldDeserializer(field);
}
else if (java.sql.Timestamp.class.equals(type)) {
deser = new SqlTimestampFieldDeserializer(field);
}
else if (java.sql.Time.class.equals(type)) {
deser = new SqlTimeFieldDeserializer(field);
}
else {
deser = new ObjectFieldDeserializer(field);
}
fieldMap.put(field.getName(), deser);
}
}
return fieldMap;
}
hashmap很明显如果此处出现了同名覆盖的字段,此处将覆盖
获取构造函数参数方法如下
/**
* Creates a map of the classes fields.
*/
protected static Object getParamArg(Class cl)
{
if (! cl.isPrimitive())
return null;
else if (boolean.class.equals(cl))
return Boolean.FALSE;
else if (byte.class.equals(cl))
return new Byte((byte) 0);
else if (short.class.equals(cl))
return new Short((short) 0);
else if (char.class.equals(cl))
return new Character((char) 0);
else if (int.class.equals(cl))
return Integer.valueOf(0);
else if (long.class.equals(cl))
return Long.valueOf(0);
else if (float.class.equals(cl))
return Float.valueOf(0);
else if (double.class.equals(cl))
return Double.valueOf(0);
else
throw new UnsupportedOperationException();
}
当构造函数为原生类型默认为0或false,否则传入null(构造函数如果是包含参数的请注意,很可能出现问题)
当读取实例时代码如下
private Object readObjectInstance(Class cl, ObjectDefinition def)
throws IOException
{
String type = def.getType();
String []fieldNames = def.getFieldNames();
if (cl != null) {
Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type, cl);
return reader.readObject(this, fieldNames);
}
else {
return findSerializerFactory().readObject(this, type, fieldNames);
}
}
明显此处fields是从hessionInput中读取(即序列化时的arraylist)
那么如果存在和父类同名的属性时,由于前面的说明会将之类的属性排在前面。那么通常在写到父类的属性通常都会为null(父类的同名字段通常都是没有赋值)
那么在读取的时候自然会将原来的已经设置的值给重新设置为null。
总结hession2存在的两个问题
- 构造函数如果包含参数,那么在hession初始化的时候将会给引用类型传入null,其他类型传入0或者false,要保证不要产生空指针
- 如果序列化的不是普通类型包含多层级的 父类,要确保子类不要包含父类同名字段,否则很容易出现子类字段在赋值后被重新赋值为空,造成字段丢失
比如如下
/**
* Created by Geekkiller on 2017/3/16.
*/
public class TmRemindSo extends So {
private static final long serialVersionUID = 1L;
/**
* 是否分页:
*/
private boolean isPage = false;
/**
* 上次保养起始日
*/
private String beginMaintainDate;
/**
* 上次保养结束日
*/
private String endMaintainDate;
/**
* 下次保养起始日
*/
private String beginNextMaintainDate;
/**
* 下次保养结束日
*/
private String endNextMaintainDate;
/**
* 客户生日起始
*/
private String beginBirthday;
/**
* 客户生日结束
*/
private String endBirthday;
private String beginLastServiceTime;
private String endLastServiceTime;
private String beginCardEndTime;
private String endCardEndTime;
private String cardName;
private String cardType;
private String beginDriverExpiryDate;
private String endDriverExpiryDate;
private String beginAnnualDate;
private String endAnnualDate;
private String idStore; //门店id
private String idOwnOrg; //组织ID
}
/**
* Created by qixiaobo on 2016/10/11.
* 数据库查询基类
*/
public class So implements Serializable, Pagable, Sortable {
private static final long serialVersionUID = 436513842210828356L;
/*
* 当前页码
*/
private int currentPage = 1;
private int pageSize = AppConstant.MIDPAGESIZE;
private Integer id;
private String pkId;
/**
* pkId的列表
*/
private List<String> pkIdList;
private String idOwnOrg;
/**
* 创建人
*/
private String creator;
private List<Sort> sorts = new LinkedList<>();
}
如上idownorg字段存在同名,在hessian环境下就会出现字段丢失