Membuat Anotasi Kustom di Java

1. Pengenalan

Anotasi Java adalah mekanisme untuk menambahkan maklumat metadata ke kod sumber kami. Mereka adalah bagian yang kuat dari Java, dan ditambahkan dalam JDK5. Anotasi menawarkan alternatif untuk penggunaan deskriptor XML dan antara muka penanda.

Walaupun kita dapat melampirkannya ke pakej, kelas, antara muka, kaedah, dan bidang, anotasi dengan sendirinya tidak mempengaruhi pelaksanaan program.

Dalam tutorial ini, kita akan memfokuskan pada cara membuat anotasi tersuai, dan bagaimana memprosesnya. Kami dapat membaca lebih lanjut mengenai anotasi dalam artikel kami mengenai asas anotasi.

2. Membuat Anotasi Tersuai

Kami akan membuat tiga anotasi tersuai dengan tujuan untuk membuat siri objek menjadi rentetan JSON.

Kami akan menggunakan yang pertama di tingkat kelas, untuk menunjukkan kepada penyusun bahawa objek kami boleh bersiri. Seterusnya, kami akan menerapkan yang kedua ke medan yang ingin kami masukkan dalam rentetan JSON.

Akhirnya, kami akan menggunakan anotasi ketiga pada tahap kaedah, untuk menentukan kaedah yang akan kami gunakan untuk menginisialisasi objek kami.

2.1. Contoh Anotasi Tahap Kelas

Langkah pertama untuk membuat anotasi tersuai adalah menyatakannya menggunakan kata kunci @interface :

public @interface JsonSerializable { }

Langkah seterusnya adalah menambahkan meta-anotasi untuk menentukan skop dan sasaran anotasi tersuai kami:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.Type) public @interface JsonSerializable { }

Seperti yang dapat kita lihat, anotasi pertama kita mempunyai keterlihatan runtime, dan kita dapat menerapkannya pada jenis (kelas) . Lebih-lebih lagi, ia tidak mempunyai kaedah, dan dengan itu berfungsi sebagai penanda sederhana untuk menandakan kelas yang dapat diselaraskan menjadi JSON.

2.2. Contoh Anotasi Tahap Padang

Dengan cara yang sama, kami membuat anotasi kedua kami, untuk menandakan bidang yang akan kami sertakan dalam JSON yang dihasilkan:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface JsonElement { public String key() default ""; }

Anotasi menyatakan satu parameter String dengan nama "key" dan string kosong sebagai nilai lalai.

Semasa membuat anotasi tersuai dengan kaedah, kita harus sedar bahawa kaedah ini tidak boleh mempunyai parameter, dan tidak dapat memberikan pengecualian . Juga, jenis pengembalian terhad kepada primitif, Rentetan, Kelas, jumlah, anotasi, dan susunan jenis ini, dan nilai lalai tidak boleh menjadi nol .

2.3. Contoh Anotasi Tahap Kaedah

Mari kita bayangkan bahawa, sebelum membuat siri objek ke rentetan JSON, kita ingin melaksanakan beberapa kaedah untuk menginisialisasi objek. Atas sebab itu, kami akan membuat anotasi untuk menandakan kaedah ini:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Init { }

Kami mengisytiharkan anotasi umum dengan keterlihatan runtime yang dapat kami gunakan untuk kaedah kelas kami.

2.4. Memohon Anotasi

Sekarang, mari kita lihat bagaimana kita dapat menggunakan anotasi tersuai kita. Sebagai contoh, mari kita bayangkan bahawa kita mempunyai objek jenis Orang yang ingin kita bersiri menjadi rentetan JSON. Jenis ini mempunyai kaedah yang memanfaatkan huruf besar dari nama depan dan nama belakang. Kami ingin memanggil kaedah ini sebelum membuat siri objek:

@JsonSerializable public class Person { @JsonElement private String firstName; @JsonElement private String lastName; @JsonElement(key = "personAge") private String age; private String address; @Init private void initNames() { this.firstName = this.firstName.substring(0, 1).toUpperCase() + this.firstName.substring(1); this.lastName = this.lastName.substring(0, 1).toUpperCase() + this.lastName.substring(1); } // Standard getters and setters }

Dengan menggunakan anotasi khusus kami, kami menunjukkan bahawa kami dapat membuat siri objek Person ke rentetan JSON. Di samping itu, output hanya boleh mengandungi medan nama pertama , nama belakang , dan umur objek tersebut. Lebih-lebih lagi, kami mahu kaedah initNames () dipanggil sebelum bersiri.

Dengan menetapkan parameter utama anotasi @JsonElement ke "personAge", kami menunjukkan bahawa kami akan menggunakan nama ini sebagai pengecam untuk bidang dalam output JSON.

Demi demonstrasi, kami membuat initNames () peribadi, jadi kami tidak dapat menginisialisasi objek kami dengan memanggilnya secara manual, dan pembangun kami juga tidak menggunakannya.

3. Memproses Anotasi

Sejauh ini, kami telah melihat bagaimana membuat anotasi tersuai dan bagaimana menggunakannya untuk menghiasi kelas Person . Sekarang, kita akan melihat bagaimana memanfaatkannya dengan menggunakan Java Reflection API.

Langkah pertama adalah untuk memeriksa sama ada objek kita kosong atau tidak, serta sama ada jenisnya mempunyai anotasi @JsonSerializable atau tidak:

private void checkIfSerializable(Object object) { if (Objects.isNull(object)) { throw new JsonSerializationException("The object to serialize is null"); } Class clazz = object.getClass(); if (!clazz.isAnnotationPresent(JsonSerializable.class)) { throw new JsonSerializationException("The class " + clazz.getSimpleName() + " is not annotated with JsonSerializable"); } }

Kemudian, kami mencari kaedah apa pun dengan anotasi @Init, dan kami melaksanakannya untuk menginisialisasi bidang objek kami:

private void initializeObject(Object object) throws Exception { Class clazz = object.getClass(); for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(Init.class)) { method.setAccessible(true); method.invoke(object); } } }

Panggilan kaedah . setAccessible ( benar) membolehkan kita untuk melaksanakan swasta initNames () kaedah .

Selepas inisialisasi, kami melakukan iterasi pada bidang objek kami, mengambil kunci dan nilai elemen JSON, dan memasukkannya ke dalam peta. Kemudian, kami membuat rentetan JSON dari peta:

private String getJsonString(Object object) throws Exception { Class clazz = object.getClass(); Map jsonElementsMap = new HashMap(); for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); if (field.isAnnotationPresent(JsonElement.class)) { jsonElementsMap.put(getKey(field), (String) field.get(object)); } } String jsonString = jsonElementsMap.entrySet() .stream() .map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"") .collect(Collectors.joining(",")); return "{" + jsonString + "}"; }

Sekali lagi, kami menggunakan ladang . setAccessible ( tru e ) kerana medan objek Orang adalah peribadi.

Kelas penyusun JSON kami menggabungkan semua langkah di atas:

public class ObjectToJsonConverter { public String convertToJson(Object object) throws JsonSerializationException { try { checkIfSerializable(object); initializeObject(object); return getJsonString(object); } catch (Exception e) { throw new JsonSerializationException(e.getMessage()); } } }

Akhirnya, kami menjalankan ujian unit untuk mengesahkan bahawa objek kami bersiri seperti yang ditentukan oleh anotasi khusus kami:

@Test public void givenObjectSerializedThenTrueReturned() throws JsonSerializationException { Person person = new Person("soufiane", "cheouati", "34"); JsonSerializer serializer = new JsonSerializer(); String jsonString = serializer.serialize(person); assertEquals( "{\"personAge\":\"34\",\"firstName\":\"Soufiane\",\"lastName\":\"Cheouati\"}", jsonString); }

4. Kesimpulan

Dalam artikel ini, kami melihat cara membuat pelbagai jenis anotasi tersuai. Kemudian kami membincangkan cara menggunakannya untuk menghias objek kami. Akhirnya, kami melihat bagaimana memprosesnya menggunakan Java Reflection API.

Seperti biasa, kod lengkap boleh didapati di GitHub.