Hessian序列化对象之CollectionSerializer(六)

Hessian集合序列化机制
本文深入探讨了Hessian框架中集合类型的序列化过程,解释了如何使用CollectionSerializer进行序列化,并介绍了序列化的主要流程及关键步骤。

在Hessian中所有的集合类型都是通过CollectionSerializer对象来进行的序列化,为什么是所有的集合类型呢,让我们看一下具体的代码段,该代码段是SerializerFactory类的loadSerializer方法中的一段代码,该方法的具体功能在前面的博客中有详细说明,loadSerializer()方法负责根据调用远程方法的参数的类型选取对应的序列化对象

我们知道Collection是所有集合类型的超接口,所以在Hessian中所有的集合类型对应的序列化对象都是CollectionSerializer

    else if (Collection.class.isAssignableFrom(cl)) {//如果Collection和cl表示的类相同,或是cl的超类或超接口,则返回true
      if (_collectionSerializer == null) {
        _collectionSerializer = new CollectionSerializer();
      }

      return _collectionSerializer;
    }

知道了什么样的Class对象使用CollectionSerializer序列化对象,下面来看看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);//代码1
    }
    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());//代码2
    }

    Iterator iter = list.iterator();
    while (iter.hasNext()) {//代码3
      Object value = iter.next();

      out.writeObject(value);
    }

    if (hasEnd)
      out.writeListEnd();
  }
我们还是先来看下主流程,如果是ArrayList或者其他实现了Serializer接口的集合会走到代码1处,writeListBegin方法负责将该集合的size写入到流中,如果是Collection的其他子类会走到代码2处,去吧就是其他类型的集合要将集合的name写到流中而ArrayList是做为集合的默认实现,将长度写到集合中后就开始写具体的内容了,代码3的while循环会遍历循环的内容然后调用HessianOutput的writeObject()方法将对象写入到流中,问题来了List中的是自定义的对象HessianOutput是如何将自定义的对象写入到流中的呢?

writeObject方法又会去根据object的类型获取对应的序列化对象,这其实是一个递归的过程,递归的结束条件就是直到写出的对象是基本类型的对象还有几个特殊的对象为止

(下篇博客会介绍自定义对象是如何序列化的)

  public void writeObject(Object object)
    throws IOException
  {
    if (object == null) {
      writeNull();
      return;
    }

    Serializer serializer;

    serializer = _serializerFactory.getSerializer(object.getClass());

    serializer.writeObject(object, this);
  }
看完了主流程,我们来看一下在CollectionSerializer的writeObject()中最上面的一行代码是out.addref(obj),如果该方法为空则直接返回,不向流中写入内容,这是怎么回事呢?

addref方法的代码如下,该方法的意思是如果有一个List对象,向流中写入了两次而且两次写入的List对象有list1==list2(注意是“==”不是equals)那么第二次就不像流中写入该对象了,直接写入该对象的ref就可以了,在反序列化的时候可以根据ref解析出该对象

public boolean addRef(Object object)
    throws IOException
  {
    if (_refs == null)
      _refs = new IdentityHashMap();

    Integer ref = (Integer) _refs.get(object);

    if (ref != null) {
      int value = ref.intValue();
      
      writeRef(value);
      return true;
    }
    else {
      _refs.put(object, new Integer(_refs.size()));
      
      return false;
    }
  }

接下来让我们看看CollectionDeserializer,首先调用createList()方法确定要创建的集合对象,具体创建哪个集合对象是从输入流中解析出来的,然后将对象加入到ref集合里,然后代码3处循环解析具体的对象,解析具体对象的过程还是一个递归的过程,最后代码4从输入流中读取结束符,然后返回解析完成的对象

  public Object readList(AbstractHessianInput in, int length)
    throws IOException
  {
    Collection list = createList();//代码1

    in.addRef(list);//代码2

    while (! in.isEnd())//代码3
      list.add(in.readObject());

    in.readEnd();//代码4

    return list;
  }



"Failed to invoke the method querySalesInvoiceStatus in the service com.internet.invoice.service.invoice.SalesInvoiceService. Tried 1 times of the providers [172.22.5.19:20880] (1/1) from the registry online.zookeeper.zhaogangrentest.com:2181 on the consumer 172.16.8.92 using the dubbo version 3.1.4. Last error is: Failed to invoke remote method: querySalesInvoiceStatus, provider: DefaultServiceInstance{serviceName='online.base.saasbillcenter.service', host='172.22.5.19', port=20880, enabled=true, healthy=true, metadata={dubbo.endpoints=[{\"port\":20880,\"protocol\":\"dubbo\"}], dubbo.metadata-service.url-params={\"connections\":\"1\",\"timeout\":\"10000\",\"version\":\"1.0.0\",\"dubbo\":\"2.0.2\",\"release\":\"3.1.4\",\"side\":\"provider\",\"port\":\"20880\",\"protocol\":\"dubbo\"}, dubbo.metadata.revision=b67669db54ffe2fc424fb99e5c393687, dubbo.metadata.storage-type=local, timestamp=1752052887204}}, cause: org.apache.dubbo.remoting.RemotingException: Failed to send response: Response [id=128, version=2.0.2, status=20, event=false, error=null, result=AppResponse [value=InvoiceResult(value=[SalesInvoiceStatusResult(salesOrderId=349140, salesOrderNo=XSDD07250620019, status=true)]), exception=null]], cause: java.lang.RuntimeException: Serialized class com.internet.invoice.result.invoice.SalesInvoiceStatusResult must implement java.io.Serializable\n Java field: protected java.lang.Object com.internet.invoice.result.InvoiceResult.value\njava.lang.RuntimeException: Serialized class com.internet.invoice.result.invoice.SalesInvoiceStatusResult must implement java.io.Serializable\n Java field: protected java.lang.Object com.internet.invoice.result.InvoiceResult.value\n\tat com.alibaba.com.caucho.hessian.io.JavaSerializer$FieldSerializer.serialize(JavaSerializer.java:304)\n\tat com.alibaba.com.caucho.hessian.io.JavaSerializer.writeInstance(JavaSerializer.java:284)\n\tat com.alibaba.com.caucho.hessian.io.JavaSerializer.writeObject(JavaSerializer.java:251)\n\tat com.alibaba.com.caucho.hessian.io.Hessian2Output.writeObject(Hessian2Output.java:412)\n\tat org.apache.dubbo.common.serialize.hessian2.Hessian2ObjectOutput.writeObject(Hessian2ObjectOutput.java:92)\n\tat org.apache.dubbo.rpc.protocol.dubbo.DubboCodec.encodeResponseData(DubboCodec.java:226)\n\tat org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encodeResponse(ExchangeCodec.java:313)\n\tat org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encode(ExchangeCodec.java:77)\n\tat org.apache.dubbo.rpc.protocol.dubbo.DubboCountCodec.encode(DubboCountCodec.java:47)\n\tat org.apache.dubbo.remoting.transport.netty4.NettyCodecAdapter$InternalEncoder.encode(NettyCodecAdapter.java:69)\n\tat io.netty.handler.codec.MessageToByteEncoder.write(MessageToByteEncoder.java:107)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:717)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:709)\n\tat io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:792)\n\tat io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:702)\n\tat io.netty.handler.timeout.IdleStateHandler.write(IdleStateHandler.java:302)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:717)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:709)\n\tat io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:792)\n\tat io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:702)\n\tat io.netty.channel.ChannelDuplexHandler.write(ChannelDuplexHandler.java:115)\n\tat org.apache.dubbo.remoting.transport.netty4.NettyServerHandler.write(NettyServerHandler.java:105)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:717)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeWriteAndFlush(AbstractChannelHandlerContext.java:764)\n\tat io.netty.channel.AbstractChannelHandlerContext$WriteTask.run(AbstractChannelHandlerContext.java:1071)\n\tat io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)\n\tat io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)\n\tat io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)\n\tat io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n\tat java.lang.Thread.run(Thread.java:750)\nCaused by: java.lang.IllegalStateException: Serialized class com.internet.invoice.result.invoice.SalesInvoiceStatusResult must implement java.io.Serializable\n\tat com.alibaba.com.caucho.hessian.io.SerializerFactory.getDefaultSerializer(SerializerFactory.java:419)\n\tat com.alibaba.com.caucho.hessian.io.SerializerFactory.getSerializer(SerializerFactory.java:393)\n\tat com.alibaba.com.caucho.hessian.io.Hessian2Output.writeObject(Hessian2Output.java:411)\n\tat com.alibaba.com.caucho.hessian.io.CollectionSerializer.writeObject(CollectionSerializer.java:92)\n\tat com.alibaba.com.caucho.hessian.io.Hessian2Output.writeObject(Hessian2Output.java:412)\n\tat com.alibaba.com.caucho.hessian.io.JavaSerializer$FieldSerializer.serialize(JavaSerializer.java:302)\n\t... 31 more\n",
最新发布
07-10
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值