Membuat Anotasi Kustom di Java

1. Perkenalan

Anotasi Java adalah mekanisme untuk menambahkan informasi metadata ke kode sumber kita. Mereka adalah bagian yang kuat dari Java, dan ditambahkan di JDK5. Anotasi menawarkan alternatif untuk penggunaan deskriptor XML dan antarmuka penanda.

Meskipun kita dapat melampirkannya ke paket, kelas, antarmuka, metode, dan bidang, anotasi itu sendiri tidak berpengaruh pada eksekusi program.

Dalam tutorial ini, kita akan fokus pada cara membuat anotasi kustom, dan cara memprosesnya. Kami dapat membaca lebih lanjut tentang anotasi di artikel kami tentang dasar-dasar anotasi.

2. Membuat Anotasi Kustom

Kami akan membuat tiga anotasi khusus dengan tujuan membuat serialisasi objek menjadi string JSON.

Kami akan menggunakan yang pertama di tingkat kelas, untuk menunjukkan kepada kompiler bahwa objek kami dapat diserialkan. Selanjutnya, kami akan menerapkan yang kedua ke bidang yang ingin kami sertakan dalam string JSON.

Terakhir, kita akan menggunakan anotasi ketiga pada tingkat metode, untuk menentukan metode yang akan kita gunakan untuk menginisialisasi objek kita.

2.1. Contoh Anotasi Tingkat Kelas

Langkah pertama untuk membuat anotasi khusus adalah mendeklarasikannya menggunakan kata kunci @interface :

public @interface JsonSerializable { }

Langkah selanjutnya adalah menambahkan meta-annotation untuk menentukan cakupan dan target anotasi kustom kami:

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

Seperti yang bisa kita lihat, anotasi pertama kita memiliki visibilitas waktu proses, dan kita bisa menerapkannya ke tipe (kelas) . Selain itu, ia tidak memiliki metode, dan dengan demikian berfungsi sebagai penanda sederhana untuk menandai kelas yang dapat diserialkan ke dalam JSON.

2.2. Contoh Anotasi Tingkat Bidang

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

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

Anotasi mendeklarasikan satu parameter String dengan nama "key" dan string kosong sebagai nilai default.

Saat membuat anotasi khusus dengan metode, kita harus menyadari bahwa metode ini tidak boleh memiliki parameter, dan tidak dapat memunculkan pengecualian . Selain itu, jenis kembalian dibatasi untuk primitif, String, Kelas, enum, anotasi, dan array jenis ini, dan nilai default tidak boleh null .

2.3. Contoh Anotasi Level Metode

Mari kita bayangkan bahwa, sebelum membuat serial objek menjadi string JSON, kami ingin menjalankan beberapa metode untuk menginisialisasi objek. Oleh karena itu, kami akan membuat anotasi untuk menandai metode ini:

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

Kami mendeklarasikan anotasi publik dengan visibilitas waktu proses yang dapat kami terapkan ke metode kelas kami.

2.4. Menerapkan Anotasi

Sekarang, mari kita lihat bagaimana kita dapat menggunakan anotasi khusus kita. Misalnya, mari kita bayangkan bahwa kita memiliki objek bertipe Person yang ingin kita serialisasi menjadi string JSON. Tipe ini memiliki metode yang menggunakan huruf besar pertama dari nama depan dan belakang. Kami ingin memanggil metode ini sebelum membuat serial 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 bahwa kami dapat membuat serial objek Person menjadi string JSON. Selain itu, keluaran harus berisi hanya bidang firstName , lastName , dan usia dari objek itu. Selain itu, kami ingin metode initNames () dipanggil sebelum serialisasi.

Dengan menyetel parameter kunci dari anotasi @JsonElement ke "personAge", kami menunjukkan bahwa kami akan menggunakan nama ini sebagai pengenal untuk bidang di keluaran JSON.

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

3. Memproses Anotasi

Sejauh ini, kita telah melihat cara membuat anotasi khusus dan cara menggunakannya untuk mendekorasi kelas Person . Sekarang, kita akan melihat bagaimana memanfaatkannya dengan menggunakan API Refleksi Java.

Langkah pertama adalah memeriksa apakah objek kita null atau tidak, serta apakah tipenya memiliki 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 metode apa pun dengan anotasi @Init, dan kami menjalankannya 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 metode . setAccessible ( benar) memungkinkan kita untuk menjalankan swasta initNames () metode .

Setelah inisialisasi, kita mengulang bidang objek kita, mengambil kunci dan nilai elemen JSON, dan meletakkannya di peta. Kemudian, kami membuat string 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 lapangan . setAccessible ( tru e ) karena Orang bidang obyek yang pribadi.

Kelas serializer 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()); } } }

Terakhir, kami menjalankan pengujian unit untuk memvalidasi bahwa objek kami diserialkan 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

Di artikel ini, kami melihat cara membuat berbagai jenis anotasi khusus. Kemudian kami membahas cara menggunakannya untuk mendekorasi objek kami. Akhirnya, kami melihat bagaimana memprosesnya menggunakan API Refleksi Java.

Seperti biasa, kode lengkap tersedia di GitHub.