Ở phần trước, các bạn đã được tìm hiểu về Java Reflection, ở phần này mình sẽ đề cập đến khái niệm khác nâng cao hơn cũng được phát triển từ Java Reflection. Đó là Annotation và cách tự tạo ra 1 annotation cho riêng mình.

Annotation được Java đưa ra từ phiên bản 1.5, tạo nên sự thay đổi lớn và là một phần quan trọng trong các framework hiện tại như Spring, Hibernate,… Những ai đã từng sử dụng Spring thì chắc chắn sẽ thấy các annotation như @Service, @Transaction,... hay ở Hibernate thì sẽ thấy các annotation như @Key, @Entity,... Hay annotation thường xuyên gặp nhất là @Override. Đó là các annotation được xây dựng sẵn trong framework giúp người dùng thuận tiện trong việc khai báo các nghiệp vụ. Thay vì truyền tham số hay duplicate code để xử lý các nghiệp vụ tương tự nhau thì chỉ cần khai báo annotation cho class/field/method là xong.

Annotation thực chất là interface nên chỉ cần khai báo field hoặc method trong đó

Các annotation tự tạo sử dụng trong bài viết

Annotation Table định danh cho 1 entity tương đương 1 table như của hibernate

package net.tunghuynh.reflect.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD,
        ElementType.PARAMETER})
public @interface MyTable {
    String table();
    String schema();
}

Annotation Field định danh cho 1 field là column của table

package net.tunghuynh.reflect.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD,
        ElementType.PARAMETER})
public @interface MyField {
    String column();
}

Annotation Key định danh cho 1 trường là Primary key. Cái này chỉ cần tạo annotation để khai báo, không cần field hay method nào thêm

package net.tunghuynh.reflect.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD,
        ElementType.PARAMETER})
public @interface MyKey {
}

Annotation RequestMapping định danh cho 1 API path tương tự trong Spring boot

package net.tunghuynh.reflect.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD,
        ElementType.PARAMETER})
public @interface MyRequestMapping {
    String value();
    String method();
}

Annotation Param định danh cho các parameter của method

package net.tunghuynh.reflect.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD,
        ElementType.PARAMETER})
public @interface MyParam {
    String value();
}

Ví dụ sử dụng annotation đã tạo cho classfield

package net.tunghuynh.reflect.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * net.tunghuynh.reflect.annotation.EntityExample
 * by Tùng Huynh
 * at 28/01/2019 8:26 PM
 */
@MyTable(table = "table", schema = "schema")
public class EntityExample {

    @MyKey
    @MyField(column = "COLUMN")
    private int column;

    public void printAnnotation(String... fields) throws Exception{
        Class<?> aClass = getClass();
        String keyColumn = "";
        String sql = "SELECT ";
        for (int i = 0; i < fields.length; i++) {

            //Lấy danh sách column theo annotation
            Field field = aClass.getDeclaredField(fields[i]);
            MyField fieldAnn = field.getAnnotation(MyField.class);
            sql +=  fieldAnn.column() + ", ";

            //Tìm trường có chứa annotation MyKey
            Annotation[] fieldAnns = field.getAnnotations();
            for (Annotation anno : fieldAnns) {
                if (anno instanceof MyKey){
                    keyColumn = fieldAnn.column();
                    break;
                }
            }
        }
        if (fields.length>0){
            sql = sql.substring(0, sql.length()-2);
        }
        //Lấy annotation của class
        MyTable myAnn = aClass.getAnnotation(MyTable.class);
        sql += String.format(" FROM %s.%s WHERE 1=1", myAnn.schema(), myAnn.table());
        System.out.println("Primary key: " + keyColumn);
        System.out.printf("SQL: " + sql);
    }

    public static void main(String[] args) throws Exception{
        new EntityExample().printAnnotation("column");
    }
}

Kết quả khi chạy

Primary key: COLUMN
SQL: SELECT COLUMN FROM schema.table WHERE 1=1

Ở đây mình chỉ viết mô phỏng in ra câu lệnh select, các bạn có thể phát triển các nghiệp vụ CRUD chi tiết hơn cho class

Giờ ta có thể tạo các entity khác kế thừa lại các nghiệp vụ chỉ bằng cách khai báo annotation rất đơn giản

package net.tunghuynh.reflect.annotation;

/**
 * net.tunghuynh.reflect.annotation.StudentExample
 * by Tùng Huynh
 * at 28/01/2019 8:30 PM
 */
@MyTable(table = "STUDENT", schema = "QLSV")
public class StudentExample extends EntityExample {

    @MyKey
    @MyField(column = "STUDENT_ID")
    private int studentId;

    @MyField(column = "NAME")
    private String name;

    public static void main(String[] args) throws Exception{
        new StudentExample().printAnnotation("studentId", "name");
    }
}

Kết quả

Primary key: STUDENT_ID
SQL: SELECT STUDENT_ID, NAME FROM QLSV.STUDENT WHERE 1=1

Ví dụ sử dụng annotation đã tạo cho methodparameter

package net.tunghuynh.reflect.annotation;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * net.tunghuynh.reflect.annotation.MethodParameterExample
 * by Tùng Huynh
 * at 28/01/2019 8:27 PM
 */
public class MethodParameterExample {

    @MyRequestMapping(value = "/doMethod", method = "GET")
    protected void doMethod(@MyParam(value = "paramName") String paramValue) { }

    public void printAnnotation(String methodName, Class... paramTypes) throws Exception{
        Class<?> aClass = getClass();

        //Lấy đối tượng method
        Method method = aClass.getDeclaredMethod(methodName, paramTypes);

        // Lấy ra danh sách các Annotation của method.
        MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
        System.out.println(String.format("Method: %s | %s", requestMapping.value(), requestMapping.method()));

        // Lấy ra danh sách các Parameter của method.
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            MyParam myParam = parameter.getAnnotation(MyParam.class);
            System.out.println(myParam.value() + ": " + parameter.getName());
        }
    }

    public static void main(String[] args) throws Exception{
        new MethodParameterExample().printAnnotation("doMethod", String.class);
    }
}

Kết quả

Method: /doMethod | GET
paramName: arg0

Các bạn có thể tự tạo cho mình các Annotation riêng tùy theo nghiệp vụ và mục đích sử dụng, giúp thuận tiện hơn khi coding, giảm thiếu duplicate code dư thừa, tăng tốc code, tăng khả năng mở rộng và nhìn code chuyên nghiệp hơn.

Chúc các bạn thành công 🙂

One Reply to “[Java] Phần 9: Tạo Annotation bằng Java Reflection”

Bình luận