Serialisasi baru dapat muncul di Java

Sebuah dokumen penelitian baru telah muncul di situs web OpenJDK yang menggambarkan gagasan memperkenalkan serialisasi baru yang ditingkatkan ke dalam bahasa untuk menggantikan yang lama.

Serialisasi di Jawa telah ada sejak versi 1.1, yaitu, hampir sejak saat ia dilahirkan. Di satu sisi, serialisasi adalah mekanisme yang sangat nyaman yang memungkinkan Anda untuk dengan cepat dan mudah membuat kelas apa saja yang dapat serial dengan mewarisi kelas ini dari antarmuka java.io.Serializable. Mungkin bahkan kesederhanaan ini telah menjadi salah satu alasan utama mengapa Java telah mendapatkan popularitas besar di dunia, karena itu memungkinkan Anda untuk menulis aplikasi jaringan dengan cepat dan efisien.

Di sisi lain, cara serialisasi diimplementasikan di Jawa melibatkan sejumlah besar masalah yang meningkatkan biaya aplikasi pendukung, mengurangi keamanannya, dan memperlambat evolusi platform.

Apa yang salah dengan serialisasi di Jawa? Kami daftar masalah yang paling serius:

  • Serialisasi (dan deserialisasi) melewati mekanisme bahasa. Itu mengabaikan pengubah akses bidang (pribadi, dilindungi) dan membuat objek tanpa menggunakan konstruktor, yang berarti ia mengabaikan invarian yang mungkin ada dalam konstruktor ini. Seorang penyerang dapat mengeksploitasi kerentanan seperti itu dengan mengganti data dengan data yang tidak valid, dan mereka akan berhasil ditelan selama deserialisasi.
  • Saat menulis kelas serial, kompiler tidak membantu dengan cara apa pun dan tidak mendeteksi kesalahan. Sebagai contoh, Anda tidak dapat secara statis menjamin bahwa semua bidang dari kelas berseri serial dapat serial sendiri. Atau Anda dapat membuat kesalahan ketik pada metode readObject, writeObject, readResolve, dll., Dan kemudian metode ini tidak akan digunakan selama serialisasi.
  • Serialisasi tidak mendukung mekanisme versi normal, sehingga sangat sulit untuk memodifikasi kelas serial agar mereka tetap kompatibel dengan versi lama mereka.
  • Serialisasi sangat terkait dengan streaming encoding / decoding, yang berarti sangat sulit untuk mengubah format encoding ke yang berbeda dari yang standar. Selain itu, format standar tidak ringkas, tidak efisien, atau dapat dibaca manusia.

Kesalahan mendasar dari serialisasi yang ada di Jawa adalah bahwa ia mencoba untuk menjadi terlalu "tidak terlihat" oleh programmer. Itu hanya mewarisi dari java.io.Serializable dan menerima beberapa sihir implisit yang dijalankan oleh mesin virtual.
Sebaliknya, programmer harus secara eksplisit menulis konstruksi yang bertanggung jawab untuk membangun dan mendekonstruksi objek. Konstruksi ini harus pada tingkat bahasa dan harus ditulis melalui akses bidang statis, bukan refleksi.

Kesalahan serialisasi lainnya adalah ia mencoba melakukan terlalu banyak. Dia mengatur dirinya sendiri tugas untuk bisa membuat serial setiap grafik objek yang sewenang-wenang (yang mungkin mengandung loop) dan membatalkan deserialisasi kembali tanpa merusak kondisinya.

Kesalahan ini dapat diperbaiki dengan menyederhanakan tugas dan membuat serialisasi bukan grafik objek, tetapi pohon data di mana tidak akan ada konsep identitas (seperti dalam JSON).

Bagaimana membuat serialisasi yang sesuai secara alami dengan model objek, menggunakan konstruktor untuk deserialisasi, dipisahkan dari format penyandian dan mendukung versi? Untuk tujuan ini , anotasi datang untuk menyelamatkan dan kemungkinan bahasa yang belum termasuk dalam Java: pencocokan pola . Sebagai contoh:

public class Range { int lo; int hi; private Range(int lo, int hi) { if (lo > hi) throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi)); this.lo = lo; this.hi = hi; } @Serializer public pattern Range(int lo, int hi) { lo = this.lo; hi = this.hi; } @Deserializer public static Range make(int lo, int hi) { return new Range(lo, hi); } } 

Dalam contoh ini, kelas Range dideklarasikan, yang siap untuk serialisasi melalui dua anggota khusus kelas: serializer dan deserializer yang ditandai dengan penjelasan @Serializer dan @Deserializer. Serializer diimplementasikan melalui dekonstruktor pola, dan deserializer diimplementasikan melalui metode statis di mana konstruktor dipanggil. Jadi, selama deserialization, hi> = invarian yang ditentukan dalam konstruktor tidak dapat dihindari diperiksa.
Tidak ada keajaiban dalam pendekatan ini, dan anotasi biasa digunakan, sehingga kerangka kerja apa pun dapat melakukan serialisasi, dan bukan hanya platform Java itu sendiri. Ini berarti bahwa format penyandian juga bisa apa saja (biner, XML, JSON, YAML, dll.).

Karena serializers dan deserializer adalah metode umum, programmer memiliki kebebasan besar dalam implementasinya. Sebagai contoh, ia dapat memilih representasi objek yang berbeda dari cara objek diwakili dalam memori. Misalnya, LinkedList dapat diserialisasi bukan menjadi rantai tautan, tetapi menjadi satu array berkelanjutan, yang akan membuat presentasi lebih sederhana, lebih efisien, dan lebih kompak.

Versi dalam pendekatan ini diimplementasikan menggunakan bidang versi khusus dari penjelasan @Serializer dan @Deserializer:

 class C { int a; int b; int c; @Deserializer(version = 3) public C(int a, int b, int c) { this a = a; this.b = b; this.c = c; } @Deserializer(version = 2) public C(int a, int b) { this(a, b, 0); } @Deserializer(version = 1) public C(int a) { this(a, 0, 0); } @Serializer(version = 3) public pattern C(int a, int b, int c) { a = this.a; b = this.b; c = this.c; } } 

Dalam contoh ini, satu dari tiga deserializer akan dipanggil, tergantung pada versinya.
Bagaimana jika kita tidak ingin serializer dan deserializer tersedia untuk orang lain selain untuk keperluan serialisasi? Untuk melakukan ini, kita dapat menjadikannya pribadi. Namun, dalam kasus ini, kerangka kerja serialisasi tertentu tidak akan dapat mengaksesnya melalui refleksi jika kode tersebut ada di dalam modul di mana paket tidak terbuka untuk akses reflektif yang mendalam. Untuk kasus seperti itu, diusulkan untuk memperkenalkan konstruksi baru lain ke dalam bahasa: anggota kelas terbuka. Sebagai contoh:

 class Foo { private final InternalState is; public Foo(ExternalState es) { this(new InternalState(es)); } @Deserializer private open Foo(InternalState is) { this.is = is; } @Serializer private open pattern serialize(InternalState is) { is = this.is; } } 

Di sini, serializers dan deserializers ditandai dengan kata kunci terbuka, yang membuatnya terbuka untuk setAccessible.

Dengan demikian, pendekatan baru pada dasarnya berbeda dari yang lama: di dalamnya, kelas dirancang sebagai serializable, dan tidak diberikan kepada platform seperti apa adanya. Ini membutuhkan usaha ekstra, tetapi membuat serialisasi lebih mudah diprediksi, lebih aman, dan independen dari format encoding dan kerangka serialisasi.

PS Friends, jika Anda ingin menerima berita serupa tentang Java lebih cepat dan nyaman, maka berlangganan saluran saya di Telegram.

Source: https://habr.com/ru/post/id455880/


All Articles