美文网首页
hessian序列化的坑

hessian序列化的坑

作者: PataPataPa_2718 | 来源:发表于2021-01-07 00:29 被阅读0次

    子类和父类不能有同名字段

    可以参看这篇文章:
    https://blog.51cto.com/tianya23/582256
    学习记录,自我总结一下

    hessian序列化对象的时候,默认使用的com.caucho.hessian.io.JavaSerializer,这里只说普通对象,
    JavaSerializerwriteObject方法会调用writeInstance方法来序列化字段,代码如下:

      @Override
      public void writeInstance(Object obj, AbstractHessianOutput out)
        throws IOException
      {
        try {
          for (int i = 0; i < _fields.length; i++) {
            Field field = _fields[i];
    
            _fieldSerializers[i].serialize(out, obj, field);
          }
        } catch (RuntimeException e) {
          throw new RuntimeException(e.getMessage() + "\n class: "
                                     + obj.getClass().getName()
                                     + " (object=" + obj + ")",
                                     e);
        } catch (IOException e) {
          throw new IOExceptionWrapper(e.getMessage() + "\n class: "
                                       + obj.getClass().getName()
                                       + " (object=" + obj + ")",
                                       e);
        }
      }
    

    有循环可以看出,这里是按照字段顺序依次序列化,而_fields又是从哪儿来的呢?
    我们找到JavaSerializer的构造方法:

      public JavaSerializer(Class<?> cl)
      {
        introspect(cl);
    
        _writeReplace = getWriteReplace(cl);
    
        if (_writeReplace != null)
          _writeReplace.setAccessible(true);
      }
    

    继续跟踪,可以看到introspect方法,它的代码片段如下:

    protected void introspect(Class<?> cl)
      {
        if (_writeReplace != null)
          _writeReplace.setAccessible(true);
    
        ArrayList<Field> primitiveFields = new ArrayList<Field>();
        ArrayList<Field> compoundFields = new ArrayList<Field>();
        //循环先获得当前类字段,然后再获得父类字段
        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中
        ArrayList<Field> fields = new ArrayList<Field>();  
        fields.addAll(primitiveFields);
        fields.addAll(compoundFields);
    
        _fields = new Field[fields.size()];
        fields.toArray(_fields);
    
        _fieldSerializers = new FieldSerializer[_fields.length];
    
        //依次获取fieldSerializer
        for (int i = 0; i < _fields.length; i++) {
          _fieldSerializers[i] = getFieldSerializer(_fields[i].getType());
        }
      }
    

    introspect方法也是先获得当前class的字段,然后再获得父类的字段。所有字段放到arraylist中,因此,序列化的时候是子类数据在前,父类数据在后。



    这时候,我们看看反序列化类com.caucho.hessian.io.JavaDeserializer,它的构造函数如下:
      public JavaDeserializer(Class<?> cl, FieldDeserializer2Factory fieldFactory)
      {
        _type = cl;
          //获取字段map
        _fieldMap = getFieldMap(cl, fieldFactory);
    
        _readResolve = getReadResolve(cl);
    
        if (_readResolve != null) {
          _readResolve.setAccessible(true);
        }
        
        _constructor = getConstructor(cl);
        _constructorArgs = getConstructorArgs(_constructor);
      }
    

    里面getFieldMap方法如下:

    protected HashMap<String,FieldDeserializer2> 
      getFieldMap(Class<?> cl, FieldDeserializer2Factory fieldFactory)
      {
        HashMap<String,FieldDeserializer2> fieldMap
          = new HashMap<String,FieldDeserializer2>();
          //for循环先获得子类字段,然后再获得父类字段
        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();
            }
        */
    
            FieldDeserializer2 deser = fieldFactory.create(field);
    
            fieldMap.put(field.getName(), deser);
          }
        }
    
        return fieldMap;
      }
    

    这里也是先获得当前类的字段,然后再获得父类的字段,但是如果子类和父类字段同名,fieldMap只会有一个字段名称key。

    在反序列化字段接收值的时候,会调用readMap:

    public Object readMap(AbstractHessianInput in, Object obj)
        throws IOException
      {
        try {
          int ref = in.addRef(obj);
    
          while (! in.isEnd()) {
            Object key = in.readObject();
            
            FieldDeserializer2 deser = _fieldMap.get(key);
    
            if (deser != null)
              deser.deserialize(in, obj);
            else
              in.readObject();
          }
          
          in.readMapEnd();
    
          Object resolve = resolve(in, obj);
    
          if (obj != resolve)
            in.setRef(ref, resolve);
    
          return resolve;
        } catch (IOException e) {
          throw e;
        } catch (Exception e) {
          throw new IOExceptionWrapper(e);
        }
      }
    

    \color{red}{重点!!!}

    \color{red}{序列化的时候,子类数据在前,父类数据在后}
    \color{red}{反序列化的时候,先读到的是子类数据,而后读的是父类数据}
    \color{red}{因此,对于同名字段,子类字段会被赋值两次,第二次会被父类字段覆盖,导致子类字段值丢失。}

    \color{red}{总结:}
    \color{red}{这个坑的原因是,序列化的时候子类数据在前,父类数据在后。} \color{red}{反序列化的时候,也是读取到子类数据,然后是父类数据,父类字段将子类字} \color{red}{段值覆盖了,从而导致子类字段丢失。}

    相关文章

      网友评论

          本文标题:hessian序列化的坑

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