JAVA自定義註解示例詳解

什麼是JAVA註解?這是百度百科的解釋:

       定義:註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,註釋。

       作用分類:

      ①編寫文檔:通過代碼裏標識的元數據生成文檔【生成文檔doc文檔】

      ②代碼分析:通過代碼裏標識的元數據對代碼進行分析【使用反射】

      ③編譯檢查:通過代碼裏標識的元數據讓編譯器能夠實現基本的編譯檢查【Override】         

      寫過JAVA代碼的人對註解都不陌生,尤其在各種框架中很常見。這些註解都非常優秀,也很容易理解,但是,爲什麼要使用註解?註解是如何工作的?讓我自定義一個註解來解釋,相信理解了這個示例以後。

      首先我們分析一個很簡單的場景,我們有一個叫Person的POJO類,代碼如下:      

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Person {
    private String name;
    private String age;
 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

        然後有這樣一個HashMap:

1
2
3
4
5
private static Map input = new HashMap();
static {
        input.put("name", "Boaz");
        input.put("age", "29");
}

         需求是把這個HashMap的值轉換爲Person這個pojo類,map中的key值和Person類中的屬性名是相同的,我們使用反射的方式來實現,完整代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Main {
    private static Map input = new HashMap();
    static {
        input.put("name", "Boaz");
        input.put("age", "29");
    }
    public static void main(String[] args) {
        Person person = Map2Pojo(input, Person.class);
        System.out.print(person);
    }
    private static <T> T Map2Pojo(Map input, Class<T> clazz) {
        Method[] methods = clazz.getMethods();
        T t;
        try {
            t = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        for (Method method : methods) {
            input.keySet().forEach((key) -> {
                if (("set" + key).equals(method.getName().toLowerCase())) {
                    try {
                        method.invoke(t, input.get(key));
                    } catch (Exception e) {
                        e.printStackTrace();
                        return;
                    }
                }
            });
        }
        return t;
    }
}


現在這個Map2Pojo工具類已經可以工作了,但是現在來了一個新Map
1
2
3
4
5
6
private static Map input = new HashMap();
 
static {
    input.put("Name", "Boaz");
    input.put("Age", "29");
}

跟之前的Map沒有什麼不同,只是key值的首字母大寫了,顯然,上面的Map2Pojo工具類這個時候就不能工作了。Map2Pojo是一個通用類,可以爲
很多Pojo轉換服務,顯然在這個類裏爲這個情況添加if-else是不合適的
這個時候就是註解登場了,我們在Person類中增加如下自定義註解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Person {
    private String name;
    private String age;
    public String getName() {
        return name;
    }
    @MapKey(KeyName = "Name")
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    @MapKey(KeyName = "Age")
    public void setAge(String age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

跟上面恩Person類相比,在Set方法上增加了註解@MapKey,這是一個自定義註解,該註解源碼如下:
1
2
3
4
5
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapKey {
    String KeyName() default "";
}
定義註解的關鍵字是@interface,該註解上還有@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)兩個註解,這兩個註解被稱爲"修飾註解的註解",從字面是就可以理解,@Target(ElementType.METHOD)表示說明該註解只能註解方法,@Retention(RetentionPolicy.RUNTIME)表示
該註解在運行時有效。
在註解體中,格式就類似於Key-Value格式,KeyName相當於就是鍵,值則可以在使用註解的時候獲取,像Person類中的@MapKey(KeyName = "Name"),這裏也賦予了一個默認值爲空字符串
註解定義和使用後,我們對Map2Pojo工具類稍作修改,修改後完整源碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Main {
    private static Map<String, String> input = new HashMap<String, String>();
    static {
        input.put("Name", "Boaz");
        input.put("Age", "29");
    }
    public static void main(String[] args) {
        Person person = Map2Pojo(input, Person.class);
        System.out.print(person);
    }
    private static <T> T Map2Pojo(Map input, Class<T> clazz) {
        Method[] methods = clazz.getMethods();
        T t;
        try {
            t = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        for (Method method : methods) {
            input.keySet().forEach((key) -> {
                if (method.getAnnotations().length == 1) {
                    MapKey mapKey = (MapKey) method.getAnnotations()[0];
                    String realKey = mapKey.KeyName();
                    if (key.equals(realKey)) {
                        try {
                            method.invoke(t, input.get(key));
                        } catch (Exception e) {
                            e.printStackTrace();
                            return;
                        }
                    }
                }
                if (("set" + key).equals(method.getName().toLowerCase())) {
                    try {
                        method.invoke(t, input.get(key));
                    } catch (Exception e) {
                        e.printStackTrace();
                        return;
                    }
                }
            });
        }
        return t;
    }
}

修改的部分在22-33行,這裏就讀取了每個Method的註解MapKey,然後獲取註解MapKey中的KeyName值來同Map中的key值進行映射。
這個示例中的註解爲運行時註解,大多數自定義註解都是運行時註解。從我的角度來理解,註解可以算做是一種增加重用的機制,在這個示例中,新增了一個註解,Person類就可以在改變註解值的情況下適配不同的Map的Key值!

發佈了30 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章