Mybatis物理分页序列化问题

最近使用dubbo和Mybatis结合使用的时候遇到了坑,dubbo协议中序列化是使用hessian来实现的,问题也就是出现在序列化中.目前用到mybaits的物理插件很多是使用mybatis-paginator或者pagehelper,这2款基本上就是存在序列化问题的.下面就来扒一扒.

看看如下代码

//代码为主要代码段,省略异常,流关闭等问题
PageList<Book> result = new PageList<Book>(new Paginator(1, 20, 200));
result.add(ebook);
result.add(book);

Hessian2Output h2o = new Hessian2Output(new FileOutputStream("h2.obj"));
h2o.writeObject(result);

Hessian2Input h2i = new Hessian2Input(new FileInputStream("h2.obj"));
Object obj = h2i.readObject();
System.out.println("serializable==>"+obj.toString());
System.out.println("serializable==>"+((PageList<Book>)obj).getPaginator());

看着这样的序列化好像是没有问题,然而最后一句却报出了转型异常.原因是反序列化后的对象是ArrayList,无法转换成PageList.看下PageList的源码继承自ArrayList,内部又自定义了一个paginator.PageHelper的物理分页返回的结果Page也是如此.然后笔者却发现JDK的ObjectInputStream和ObjectOutputStream却不存在这样的问题.遂查看hessian的序列化源码

//SerializerFactory 的loadSerializer
protected Serializer loadSerializer(Class<?> cl)
    throws HessianProtocolException
  {
    Serializer serializer = null;

    for (int i = 0;
         _factories != null && i < _factories.size();
         i++) {
      AbstractSerializerFactory factory;

      factory = (AbstractSerializerFactory) _factories.get(i);

      serializer = factory.getSerializer(cl);

      if (serializer != null)
        return serializer;
    }

    serializer = _contextFactory.getSerializer(cl.getName());

    if (serializer != null)
      return serializer;

    ClassLoader loader = cl.getClassLoader();

    if (loader == null)
      loader = _systemClassLoader;

    ContextSerializerFactory factory = null;

    factory = ContextSerializerFactory.create(loader);

    serializer = factory.getCustomSerializer(cl);

    if (serializer != null) {
      return serializer;
    }
    
    if (HessianRemoteObject.class.isAssignableFrom(cl)) {
      return new RemoteSerializer();
    }
    else if (BurlapRemoteObject.class.isAssignableFrom(cl)) {
      return new RemoteSerializer();
    }
    else if (InetAddress.class.isAssignableFrom(cl)) {
      return InetAddressSerializer.create();
    }
    else if (JavaSerializer.getWriteReplace(cl) != null) {
      Serializer baseSerializer = getDefaultSerializer(cl);
      
      return new WriteReplaceSerializer(cl, getClassLoader(), baseSerializer);
    }
    else if (Map.class.isAssignableFrom(cl)) {
      if (_mapSerializer == null)
        _mapSerializer = new MapSerializer();

      return _mapSerializer;
    }
    else if (Collection.class.isAssignableFrom(cl)) {
      if (_collectionSerializer == null) {
        _collectionSerializer = new CollectionSerializer();
      }

      return _collectionSerializer;
    }

    else if (cl.isArray())
      return new ArraySerializer();

    else if (Throwable.class.isAssignableFrom(cl))
      return new ThrowableSerializer(cl, getClassLoader());

    else if (InputStream.class.isAssignableFrom(cl))
      return new InputStreamSerializer();

    else if (Iterator.class.isAssignableFrom(cl))
      return IteratorSerializer.create();

    else if (Calendar.class.isAssignableFrom(cl))
      return CalendarSerializer.SER;
    
    else if (Enumeration.class.isAssignableFrom(cl))
      return EnumerationSerializer.create();

    else if (Enum.class.isAssignableFrom(cl))
      return new EnumSerializer(cl);

    else if (Annotation.class.isAssignableFrom(cl))
      return new AnnotationSerializer(cl);

    return getDefaultSerializer(cl);
  }

从上面代码我们可以看出当我们传入PageList的时候,那么他就认为是一个collection,然后返回的是CollectionSerializer,也就是使用CollectionSerializer来序列化PageList

//CollectionSerializer类的writeObject方法
public void writeObject(Object obj, AbstractHessianOutput out)
    throws IOException
  {
    if (out.addRef(obj))
      return;

    Collection list = (Collection) obj;

    Class cl = obj.getClass();
    boolean hasEnd;
    
    if (cl.equals(ArrayList.class)
        || ! Serializable.class.isAssignableFrom(cl)) {
      hasEnd = out.writeListBegin(list.size(), null);
    }
    else if (! _sendJavaType) {
      hasEnd = false;
      
      // hessian/3a19
      for (; cl != null; cl = cl.getSuperclass()) {
        if (cl.getName().startsWith("java.")) {
          hasEnd = out.writeListBegin(list.size(), cl.getName());
          break;
        }
      }
      
      if (cl == null)
        hasEnd = out.writeListBegin(list.size(), null);
    }
    else {
      hasEnd = out.writeListBegin(list.size(), obj.getClass().getName());
    }

    Iterator iter = list.iterator();
    while (iter.hasNext()) {
      Object value = iter.next();

      out.writeObject(value);
    }

    if (hasEnd)
      out.writeListEnd();
  }

然而你看,人家CollectionSerializer才不管你集合是不是有其他对象,直接遍历去序列化.so,序列化之后就是丢失了原本的信息.类似的情况出现在分页参数对象PageBounds,由于他继承自org.apache.ibatis.session.RowBounds,框架本身这个类就没有实现序列化接口,所以它的子类继承自该类的信息在序列化之后会丢失.故而导致其信息也是一并丢失.所以,有相关的需要还需要自己来封装.



留个爪印