美文网首页
Dubbo序列化之hessian2

Dubbo序列化之hessian2

作者: 3c69b7c624d9 | 来源:发表于2017-11-30 23:00 被阅读306次

    根据前面的说明可以知道序列化功能依然使用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环境下就会出现字段丢失

    相关文章

      网友评论

          本文标题:Dubbo序列化之hessian2

          本文链接:https://www.haomeiwen.com/subject/hqobbxtx.html