kryo序列化使用及踩坑过程

2019-12-05  本文已影响0人  chanyi

kryo序列化使用及采坑

1、kryo序列化使用过程

(1)、导入依赖

<dependency>

<groupId>com.esotericsoftware</groupId>

<artifactId>kryo</artifactId>

<version>5.0.0-RC4</version>

</dependency>

(2)、实现序列化和反序列的接口

接口:

package com.simba.service;

public interface Serializer {

  /**
   * 序列化
   * @param object
   * @param bytes
   */
  void serializer(Object object,byte[] bytes);

  /**
   *
   * @param object
   * @param bytes
   * @param offset
   * @param count
   */
  void serializer(Object object,byte[] bytes,int offset,int count);

  /**
   * 反序列化
   * @param bytes
   * @param <T>
   * @return
   */
  <T> T deserializer(byte[] bytes);

  /**
   * 反序列化
   * @param bytes
   * @param offset
   * @param count
   * @param <T>
   * @return
   */
  <T> T deserializer(byte[] bytes,int offset,int count);
}

实现类:

package com.simba.service.impl;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.BeanSerializer;
import com.simba.service.Serializer;
import java.io.OutputStream;
import org.objenesis.strategy.StdInstantiatorStrategy;
import org.springframework.stereotype.Component;


/**
 * kryo实现序列化和反序列化接口
 * kryo不是线程安全的,需要注意,使用独立线程实现
 */
@Component
public class KryoSerializer implements Serializer {

  //将kryo对象存储在线程中,只有这个线程可以访问到,这样保证kryo的线程安全性,ThreadLocal(线程内部存储类)
  //通过get()&set()方法读取线程内的数据
  private final ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>(){
    @Override
    protected Kryo initialValue() {
      Kryo kryo = new Kryo();
      kryo.register(aClass, new BeanSerializer<>(kryo,aClass));
      //引用,对A对象序列化时,默认情况下kryo会在每个成员对象第一次序列化时写入一个数字,
      // 该数字逻辑上就代表了对该成员对象的引用,如果后续有引用指向该成员对象,
      // 则直接序列化之前存入的数字即可,而不需要再次序列化对象本身。
      // 这种默认策略对于成员存在互相引用的情况较有利,否则就会造成空间浪费
      // (因为没序列化一个成员对象,都多序列化一个数字),
      // 通常情况下可以将该策略关闭,kryo.setReferences(false);
      kryo.setReferences(false);
      //设置是否注册全限定名,
      kryo.setRegistrationRequired(false);
      //设置初始化策略,如果没有默认无参构造器,那么就需要设置此项,使用此策略构造一个无参构造器
      kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
      return kryo;
    }
  };

  private final  ThreadLocal<Output> outputThreadLocal = new ThreadLocal<>();

  private final  ThreadLocal<Input> inputThreadLocal = new ThreadLocal<>();

  private Class<?> aClass = null;


  public Class<?> getaClass() {
    return aClass;
  }

  public void setaClass(Class<?> aClass) {
    this.aClass = aClass;
  }

  @Override
  public void serializer(Object object, byte[] bytes) {
    Kryo kryo = kryoThreadLocal.get();
    Output output =getOutPut(bytes);
    kryo.writeObjectOrNull(output,object,object.getClass());
    output.flush();
  }

  @Override
  public void serializer(Object object, byte[] bytes, int offset, int count) {
    Kryo kryo = kryoThreadLocal.get();
    Output output =getOutPut(bytes,offset,count);
    kryo.writeObjectOrNull(output,object,object.getClass());
    output.flush();
  }

  @Override
  public <T> T deserializer(byte[] bytes) {
    Kryo kryo =  kryoThreadLocal.get();
    Input input = getInPut(bytes);
    return (T)kryo.readObjectOrNull(input,aClass);
  }

  @Override
  public <T> T deserializer(byte[] bytes, int offset, int count){
    Kryo kryo = kryoThreadLocal.get();
    Input input = getInPut(bytes,offset,count);
    return (T)kryo.readObjectOrNull(input,aClass);
  }

  private Output getOutPut(byte[] bytes){
    Output output = outputThreadLocal.get();
    if(output == null){
      output = new Output();
      outputThreadLocal.set(new Output());
    }
    if(bytes!=null){
      output.setBuffer(bytes);
    }
    return output;
  }
  private Output getOutPut(byte[] bytes,int offset,int count){
    Output output = outputThreadLocal.get();
    if(output == null){
      output = new Output();
      outputThreadLocal.set(new Output());
    }
    if(bytes!=null){
      output.writeBytes(bytes,offset,count);
    }
    return output;
  }
  private Input getInPut(byte[] bytes){
    Input input = inputThreadLocal.get();
    if(input == null){
      input= new Input();
      outputThreadLocal.set(new Output());
    }
    if(bytes!=null){
      input.setBuffer(bytes);
    }
    return input;
  }

  private Input getInPut(byte[] bytes,int offser,int count){
    Input input = inputThreadLocal.get();
    if(input == null){
      input= new Input();
      outputThreadLocal.set(new Output());
    }
    if(bytes!=null){
      input.setBuffer(bytes,offser,count);
    }
    return input;
  }
}

(3)、测试代码

测试代码:

package com.simba.controller;


import com.simba.model.SubTestSerialization;
import com.simba.model.TestSerialization;
import com.simba.service.impl.KryoSerializer;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/kryo")
/**
 * kryo序列化类,是有
 */
public class KryoController {

  @RequestMapping("/test")
  public String test(){
    return "test";
  }

  @Test
  public void kayoSerializer(){
    byte[] bytes =new byte[200];
    String[] strings = {"s","s1"};
    System.out.println(Arrays.toString(bytes));
    KryoSerializer kryoSerializer = new KryoSerializer();
    kryoSerializer.setaClass(TestSerialization.class);
    TestSerialization testSerialization = new TestSerialization();
    testSerialization.setText("aaaaasdwe");
    testSerialization.setName("f");
    testSerialization.setId(999);
    testSerialization.setFlag(false);
    testSerialization.setList(Arrays.asList(strings));
    SubTestSerialization subTestSerialization = new SubTestSerialization();
    subTestSerialization.setName("test");
    testSerialization.setSubTestSerialization(subTestSerialization);

    kryoSerializer.serializer(testSerialization,bytes);
    System.out.println(testSerialization.toString());
    System.out.println(Arrays.toString(bytes));
    System.out.println("=====================================");
    TestSerialization testSerialization1 = kryoSerializer.deserializer(bytes);
    System.out.println(testSerialization1.toString());
    System.out.println(Arrays.toString(bytes));
  }

}

2、踩坑过程

(1)、com.esotericsoftware.kryo.KryoException: java.lang.NullPointerException Serialization trace:

list (com.simba.model.TestSerialization)

此问题出现的原因是类TestSerialization中有ArrayList的属性list,导致在解码的过程中程序无法解析

解决方案:

使用最新版的pom文件即可,现阶段官方文档(https://github.com/EsotericSoftware/kryo/blob/master/README.md

中描述的最新的pom文件是<version>5.0.0-RC4</version>。

(2)、在初始kryo的时候,一定要注意kryo的各种配置

最重要的单个配置为:(详细的说明在代码中有说明)

kryo.setReferences(false);

kryo.setRegistrationRequired(false);

kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());

(3)、其他问题可以直接看官网文档

https://github.com/EsotericSoftware/kryo/blob/master/README.md

上一篇 下一篇

猜你喜欢

热点阅读