序列化之Kryo

入門

  1. Kryo(github地址)是一個快速,高效的對象圖序列化Java框架。該項目的目標是速度,效率和易於使用的API。無論何時需要將對象持久化到文件,數據庫還是通過網絡,該項目都非常有用。

  2. Kryo還可以執行自動的深層和淺層複製/克隆。這是直接從對象複製到對象,而不是對象->字節->對象

  3. 依賴

    compile group: 'com.esotericsoftware', name: 'kryo', version: '4.0.1'
    
  4. 由於其底層依賴於 ASM 技術,與 Spring 等框架可能會發生 ASM 依賴的版本衝突,所以提供了另外一個依賴以供解決此問題

    compile group: 'com.esotericsoftware', name: 'kryo-shaded', version: '4.0.1'
    
  5. 簡單操作

    
    public class Person {
        private String username = "jannal";
        private String password = "123";
        ...省略getter setter...
    }  
    
    @Test
    public void case1() throws FileNotFoundException {
        Kryo kryo = new Kryo();
        Output output = new Output(new FileOutputStream("person.db"));
        Person person = new Person();
        kryo.writeObject(output, person);
        output.close();
        Input input = new Input(new FileInputStream("person.db"));
        person = kryo.readObject(input, Person.class);
        input.close();
        assert "jannal".equals(person.getUsername());
    }
    

讀寫方式

  1. Kryo 共支持三種讀寫方式

  2. 如果類是已知的,並且對象不爲null

    kryo.writeObject(output, someObject);
    // ...
    SomeClass someObject = kryo.readObject(input, SomeClass.class);
    
  3. 如果類是已知的,並且對象可能爲null

    kryo.writeObjectOrNull(output, someObject);
    // ...
    SomeClass someObject = kryo.readObjectOrNull(input, SomeClass.class);
    
  4. 如果類的字節碼未知,並且對象可能爲 null

    kryo.writeClassAndObject(output, object);
    // ...
    Object object = kryo.readClassAndObject(input);
    if (object instanceof SomeClass) {
       // ...
    }
    

API介紹

  1. 某些類無法由Kryo序列化時可以使用

    //類實現了Serializable
    kryo.register(SomeClass.class, new JavaSerializer());
    
    //類實現了Externalizable
    kryo.register(SomeClass.class, new ExternalizableSerializer());
    
  2. 自定義序列化處理器

    
    @Test
    public void case3() throws FileNotFoundException {
        Kryo kryo = new Kryo();
        kryo.register(Student.class, new StudentSerializer());
        Output output = new Output(new FileOutputStream("student.db"));
        Student student = new Student("jannal");
        kryo.writeObject(output, student);
        output.close();
        Input input = new Input(new FileInputStream("student.db"));
        student = kryo.readObject(input, Student.class);
        input.close();
        Assert.assertEquals("jannal", student.getName());
        Assert.assertEquals("jannal", student.getPerson().getUsername());
    }
    
    
    public class StudentSerializer extends Serializer<Student> {
        /**
         * 對象寫入字節
         */
        @Override
        public void write(Kryo kryo, Output output, Student student) {
            output.writeString(student.getName());
            output.writeString(student.getPerson().getUsername());
            output.writeString(student.getPerson().getPassword());
        }
    
        /**
         * 從字節中讀取轉換爲對象
         */
        @Override
        public Student read(Kryo kryo, Input input, Class<Student> clazz) {
            String name = input.readString();
            String username = input.readString();
            String password = input.readString();
            Student student = new Student(name);
            Person person = new Person();
            person.setUsername(username);
            person.setPassword(password);
            student.setPerson(person);
            return student;
        }
    }
    
    
  3. Kryo 支持對註冊行爲,當Kryo寫出對象的實例時,需要寫出一些東西來識別標識對象類,默認情況下使用類的權限定名稱和寫入對象的字節,使用類名效率不高,可以提前註冊類的標識,使用可變長度int保存。反序列化期間,註冊的類必須具有與序列化期間完全相同的ID

  4. 當ID爲較小的正整數時,它們的寫入效率最高。負ID無法有效地序列化。默認情況下原生類型、包裝類型、字符串、void使用0-9.可以將Kryo#setRegistrationRequired設置爲true,在遇到任何未註冊的類時引發異常,這樣可以防止應用程序意外使用類名字符串。

    
    @Test
    public void testRegister() throws FileNotFoundException {
        Kryo kryo = new Kryo();
        //id不能是負數
        kryo.register(Person.class, 10);
        Output output = new Output(new FileOutputStream("person.db"));
        Person person = new Person();
        kryo.writeObject(output, person);
        output.close();
        Input input = new Input(new FileInputStream("person.db"));
        person = kryo.readObject(input, Person.class);
        input.close();
        Assert.assertEquals("jannal", person.getUsername());
    }
    
  5. 缺點

    • Kryo 不支持 Bean 中增刪字段
    • 使用 Arrays.asList(); 創建的 List 對象,會引起序列化異常
    • 不支持包含無參構造器類的反序列化
    • Kryo 是線程不安全的,每當需要序列化和反序列化時都需要實例化一次或者通過ThreadLocal

kryo-serializers

  1. 使用第三方庫對 Kryo 進行序列化類型的擴展kryo-serializers

    compile group: 'de.javakaffee', name: 'kryo-serializers', version: '0.45'
    
  2. 手動註冊

    kryo.register( Arrays.asList( "" ).getClass(), new ArraysAsListSerializer() );
    kryo.register( Collections.EMPTY_LIST.getClass(), new CollectionsEmptyListSerializer() );
    kryo.register( Collections.EMPTY_MAP.getClass(), new CollectionsEmptyMapSerializer() );
    kryo.register( Collections.EMPTY_SET.getClass(), new CollectionsEmptySetSerializer() );
    kryo.register( Collections.singletonList( "" ).getClass(), new CollectionsSingletonListSerializer() );
    kryo.register( Collections.singleton( "" ).getClass(), new CollectionsSingletonSetSerializer() );
    kryo.register( Collections.singletonMap( "", "" ).getClass(), new CollectionsSingletonMapSerializer() );
    kryo.register( GregorianCalendar.class, new GregorianCalendarSerializer() );
    kryo.register( InvocationHandler.class, new JdkProxySerializer() );
    UnmodifiableCollectionsSerializer.registerSerializers( kryo );
    SynchronizedCollectionsSerializer.registerSerializers( kryo );
    
    // custom serializers for non-jdk libs
    
    // register CGLibProxySerializer, works in combination with the appropriate action in handleUnregisteredClass (see below)
    kryo.register( CGLibProxySerializer.CGLibProxyMarker.class, new CGLibProxySerializer( kryo ) );
    // dexx
    ListSerializer.registerSerializers( kryo );
    MapSerializer.registerSerializers( kryo );
    SetSerializer.registerSerializers( kryo );
    // joda DateTime, LocalDate, LocalDateTime and LocalTime
    kryo.register( DateTime.class, new JodaDateTimeSerializer() );
    kryo.register( LocalDate.class, new JodaLocalDateSerializer() );
    kryo.register( LocalDateTime.class, new JodaLocalDateTimeSerializer() );
    kryo.register( LocalDateTime.class, new JodaLocalTimeSerializer() );
    // protobuf
    kryo.register( SampleProtoA.class, new ProtobufSerializer() ); // or override Kryo.getDefaultSerializer as shown below
    // wicket
    kryo.register( MiniMap.class, new MiniMapSerializer() );
    // guava ImmutableList, ImmutableSet, ImmutableMap, ImmutableMultimap, ImmutableTable, ReverseList, UnmodifiableNavigableSet
    ImmutableListSerializer.registerSerializers( kryo );
    ImmutableSetSerializer.registerSerializers( kryo );
    ImmutableMapSerializer.registerSerializers( kryo );
    ImmutableMultimapSerializer.registerSerializers( kryo );
    ImmutableTableSerializer.registerSerializers( kryo );
    ReverseListSerializer.registerSerializers( kryo );
    UnmodifiableNavigableSetSerializer.registerSerializers( kryo );
    // guava ArrayListMultimap, HashMultimap, LinkedHashMultimap, LinkedListMultimap, TreeMultimap, ArrayTable, HashBasedTable, TreeBasedTable
    ArrayListMultimapSerializer.registerSerializers( kryo );
    HashMultimapSerializer.registerSerializers( kryo );
    LinkedHashMultimapSerializer.registerSerializers( kryo );
    LinkedListMultimapSerializer.registerSerializers( kryo );
    TreeMultimapSerializer.registerSerializers( kryo );
    ArrayTableSerializer.registerSerializers( kryo );
    HashBasedTableSerializer.registerSerializers( kryo );
    TreeBasedTableSerializer.registerSerializers( kryo );
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章