文档章节

Dubbo序列化之hessian2

Mr_Qi
 Mr_Qi
发布于 2017/06/29 22:53
字数 1634
阅读 684
收藏 2

根据前面的说明可以知道序列化功能依然使用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());
   }
 }
  1. 首先查找是否包含writeReplace方法
  2. 将所有的包含超类的字段全部放入arrayList中(去除静态和瞬态),先放入原生类型后放入包装类型
  3. 根据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]);
     }
   }
 }
  1. 获取字段集合
  2. 获取readResolve方法
  3. 获取最佳构造函数
     

获取字段集合代码如下

/**
  * 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存在的两个问题

  1. 构造函数如果包含参数,那么在hession初始化的时候将会给引用类型传入null,其他类型传入0或者false,要保证不要产生空指针
  2. 如果序列化的不是普通类型包含多层级的 父类,要确保子类不要包含父类同名字段,否则很容易出现子类字段在赋值后被重新赋值为空,造成字段丢失
     

比如如下

/**
 * 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环境下就会出现字段丢失

© 著作权归作者所有

共有 人打赏支持
Mr_Qi

Mr_Qi

粉丝 280
博文 359
码字总数 369228
作品 0
南京
程序员
私信 提问
dubbo典型协议、传输组件、序列化方式组合性能对比测试

前言 Dubbo作为一个扩展能力极强的分布式服务框架,在实现rpc特性的时候,给传输协议、传输框架和序列化方式提供了多种扩展实现,供开发者根据实际场景进行选择。 1、支持常见的传输协议:R...

杨武兵
2016/06/13
2.6K
6
Dubbo 扩展 - Dubbox

Dubbo是一个被国内很多互联网公司广泛使用的开源分布式服务框架,即使从国际视野来看应该也是一个非常全面的SOA基础框架。作为一个重要的技术研究课题,在当当网我们根据自身的需求,为Dubbo...

匿名
2014/10/23
0
11
dubbox学习

Dubbo是一个被国内很多互联网公司广泛使用的开源分布式服务框架,即使从国际视野来看应该也是一个非常全面的SOA基础框架。作为一个重要的技术研究课题,在当当网我们根据自身的需求,为Dubbo...

HI曲奇饼干
2016/01/21
272
2
Dubbo 源码解读 —— 可支持序列化及自定义扩展

一、概述 从源码中,我们可以看出来。目前,Dubbo 内部提供了 5 种序列化的方式,分别为 fastjson、Hessian2、Kryo、fst 及 Java原生支持的方式 。 针对不同的序列化方式,对比内容如下: 名...

Ryan-瑞恩
2018/07/16
0
0
当当网开源 Dubbox,扩展 Dubbo 服务框架支持REST风格远程调用

当当网近日开源了Dubbox项目,可为Dubbo服务框架提供多项扩展功能,包括REST风格远程调用、Kryo/FST序列化等等。 当当网架构部和技术委员会架构师沈理向InfoQ中文站介绍了Dubbox项目,开发背...

空云万里晴
2014/10/23
20.4K
13

没有更多内容

加载失败,请刷新页面

加载更多

eggjs与sequelize简单demo

参考 egg 官方文档 安装 // 依赖npm install --save egg-sequelize mysql2// ts 类型npm install --save @types/sequelize 插件,config/plugin.ts import { EggPlugin } from 'egg';......

Geeyu
39分钟前
1
0
看过上百部片子的这个人教你视频标签算法解析

本文由云+社区发表 随着内容时代的来临,多媒体信息,特别是视频信息的分析和理解需求,如图像分类、图像打标签、视频处理等等,变得越发迫切。目前图像分类已经发展了多年,在一定条件下已经...

腾讯云加社区
54分钟前
2
0
2. 红黑树

定义:红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树(Binary Search Tree)。 要理解红黑树,先要了解什么是二叉查找树。在上一章中,我们学习了什么是二叉树,以及二叉树...

火拳-艾斯
55分钟前
3
0
input的button类型,点击页面跳转

一、input type=button 不做任何操作 例如: <input type="button" class="btn btn-primary" style="width: 30%" value="返回" onclick="window.location.href='/users/list'"></input> onc......

Sunki
今天
1
0
踩坑:js 小数运算出现精度问题

背景 在学习小程序商城源码时发现了这个问题,单价可能出现小数,小数之间运算结果会莫名其妙多出一大串数字,比如下面这样👇。 在此之前我是知道 js 中著名的 0.1 + 0.2 != 0.3 的问题的,...

dkvirus
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部