Tạo chú thích tùy chỉnh trong Java

1. Giới thiệu

Chú thích Java là một cơ chế để thêm thông tin siêu dữ liệu vào mã nguồn của chúng tôi. Chúng là một phần mạnh mẽ của Java và đã được thêm vào JDK5. Chú thích cung cấp một giải pháp thay thế cho việc sử dụng các bộ mô tả XML và các giao diện đánh dấu.

Mặc dù chúng ta có thể đính kèm chúng vào các gói, lớp, giao diện, phương thức và trường, bản thân các chú thích không ảnh hưởng đến việc thực thi chương trình.

Trong hướng dẫn này, chúng ta sẽ tập trung vào cách tạo chú thích tùy chỉnh và cách xử lý chúng. Chúng ta có thể đọc thêm về chú thích trong bài viết của chúng tôi về những điều cơ bản về chú thích.

2. Tạo chú thích tùy chỉnh

Chúng tôi sẽ tạo ba chú thích tùy chỉnh với mục tiêu tuần tự hóa một đối tượng thành một chuỗi JSON.

Chúng tôi sẽ sử dụng cái đầu tiên ở cấp độ lớp, để cho trình biên dịch biết rằng đối tượng của chúng tôi có thể được tuần tự hóa. Tiếp theo, chúng tôi sẽ áp dụng cái thứ hai cho các trường mà chúng tôi muốn đưa vào chuỗi JSON.

Cuối cùng, chúng tôi sẽ sử dụng chú thích thứ ba ở cấp phương thức, để chỉ định phương thức mà chúng tôi sẽ sử dụng để khởi tạo đối tượng của mình.

2.1. Ví dụ về chú thích cấp lớp

Bước đầu tiên để tạo chú thích tùy chỉnh là khai báo nó bằng cách sử dụng từ khóa @interface :

public @interface JsonSerializable { }

Bước tiếp theo là thêm chú thích meta để chỉ định phạm vi và mục tiêu của chú thích tùy chỉnh của chúng tôi:

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

Như chúng ta có thể thấy, chú thích đầu tiên của chúng ta có khả năng hiển thị thời gian chạy và chúng ta có thể áp dụng nó cho các kiểu (lớp) . Hơn nữa, nó không có phương thức, và do đó nó đóng vai trò như một điểm đánh dấu đơn giản để đánh dấu các lớp có thể được tuần tự hóa thành JSON.

2.2. Ví dụ về chú thích cấp trường

Theo cách tương tự, chúng tôi tạo chú thích thứ hai, để đánh dấu các trường mà chúng tôi sẽ đưa vào JSON đã tạo:

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

Chú thích khai báo một tham số Chuỗi có tên là "khóa" và một chuỗi trống làm giá trị mặc định.

Khi tạo các chú thích tùy chỉnh bằng các phương thức, chúng ta nên biết rằng các phương thức này không được có tham số và không thể đưa ra một ngoại lệ . Ngoài ra, các kiểu trả về bị hạn chế đối với các kiểu nguyên thủy, Chuỗi, Lớp, enums, chú thích và mảng thuộc các kiểu này và giá trị mặc định không được để trống .

2.3. Ví dụ về chú thích mức phương pháp

Hãy tưởng tượng rằng, trước khi tuần tự hóa một đối tượng thành một chuỗi JSON, chúng ta muốn thực thi một số phương thức để khởi tạo một đối tượng. Vì lý do đó, chúng tôi sẽ tạo một chú thích để đánh dấu phương pháp này:

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

Chúng tôi đã khai báo một chú thích công khai với khả năng hiển thị thời gian chạy mà chúng tôi có thể áp dụng cho các phương thức của lớp mình.

2.4. Áp dụng chú thích

Bây giờ, hãy xem cách chúng ta có thể sử dụng các chú thích tùy chỉnh của mình. Ví dụ, hãy tưởng tượng rằng chúng ta có một đối tượng kiểu Person mà chúng ta muốn tuần tự hóa thành một chuỗi JSON. Loại này có phương thức viết hoa chữ cái đầu tiên của họ và tên. Chúng tôi sẽ muốn gọi phương thức này trước khi tuần tự hóa đối tượng:

@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 }

Bằng cách sử dụng các chú thích tùy chỉnh của chúng tôi, chúng tôi chỉ ra rằng chúng tôi có thể tuần tự hóa một đối tượng Person thành một chuỗi JSON. Ngoài ra, đầu ra chỉ nên chứa các trường firstName , lastNameage của đối tượng đó. Hơn nữa, chúng tôi muốn phương thức initNames () được gọi trước khi tuần tự hóa.

Bằng cách đặt tham số chính của chú thích @JsonElement thành “personAge”, chúng tôi cho biết rằng chúng tôi sẽ sử dụng tên này làm định danh cho trường trong đầu ra JSON.

Để chứng minh, chúng tôi đã đặt initNames () ở chế độ riêng tư, vì vậy chúng tôi không thể khởi tạo đối tượng của mình bằng cách gọi nó theo cách thủ công và các hàm tạo của chúng tôi cũng không sử dụng nó.

3. Xử lý chú thích

Cho đến nay, chúng ta đã thấy cách tạo các chú thích tùy chỉnh và cách sử dụng chúng để trang trí lớp Person . Bây giờ, chúng ta sẽ xem cách tận dụng chúng bằng cách sử dụng API phản chiếu của Java.

Bước đầu tiên sẽ là kiểm tra xem đối tượng của chúng ta có rỗng hay không, cũng như liệu kiểu của nó có chú thích @JsonSerializable hay không:

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"); } }

Sau đó, chúng tôi tìm kiếm bất kỳ phương thức nào có chú thích @Init và chúng tôi thực thi nó để khởi tạo các trường của đối tượng:

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); } } }

Lời gọi của phương thức . setAccessible ( true) cho phép chúng ta thực thi phương thức private initNames () .

Sau khi khởi tạo, chúng tôi lặp lại các trường của đối tượng, truy xuất khóa và giá trị của các phần tử JSON và đưa chúng vào bản đồ. Sau đó, chúng tôi tạo chuỗi JSON từ bản đồ:

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 + "}"; }

Một lần nữa, chúng tôi đã sử dụng trường . setAccessible ( tru e ) vì các trường của đối tượng Person là riêng tư.

Lớp serializer JSON của chúng tôi kết hợp tất cả các bước trên:

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

Cuối cùng, chúng tôi chạy kiểm tra đơn vị để xác thực rằng đối tượng của chúng tôi đã được tuần tự hóa như được xác định bởi các chú thích tùy chỉnh của chúng tôi:

@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. Kết luận

Trong bài viết này, chúng ta đã biết cách tạo các loại chú thích tùy chỉnh khác nhau. Sau đó, chúng tôi thảo luận về cách sử dụng chúng để trang trí các đồ vật của chúng tôi. Cuối cùng, chúng tôi đã xem xét cách xử lý chúng bằng cách sử dụng API phản chiếu của Java.

Như mọi khi, mã hoàn chỉnh có sẵn trên GitHub.