詳解Java編程中Annotation注解對(duì)象的使用方法
注解(也被稱(chēng)為元數(shù)據(jù))為我們?cè)诖a中添加信息提供了一種形式化的方法,使我們可以在稍后某個(gè)時(shí)刻非常方便地使用這些數(shù)據(jù)。
1.基本語(yǔ)法
Java SE5內(nèi)置三種標(biāo)準(zhǔn)注解
@Override:表示當(dāng)前的方法定義將覆蓋超類(lèi)中的方法.如果你不小心拼寫(xiě)錯(cuò)誤,或者方法簽名對(duì)不上被覆
蓋的方法,編譯器就會(huì)發(fā)出錯(cuò)誤提示
@Deprecated:如果程序員使用了注解為它的元素,那么編譯器就會(huì)發(fā)出警告信息
@SupperessWarnings:關(guān)閉不當(dāng)?shù)木幾g器警告信息.
Java SE5內(nèi)置四種元注解
@Target:表示該注解可以用于什么地方.可能的ElementType參數(shù)包括:
1)CONSTRUCTOR:構(gòu)造器的聲明
2)FIELD:域聲明(包括enum實(shí)例)
3)LOCAL_VARIABLE:局部變量聲明
4)METHOD:方法聲明
5)PACKAGE:包聲明
6)PARAMETER:參數(shù)聲明
7)TYPE:類(lèi)、接口(包括注解類(lèi)型)或enum聲明
@Retention:表示需要在什么級(jí)別保存該注解信息.可選的RetentionPolicy參數(shù)包括:
1)SOURCE:注解將被編譯器丟棄
2)CLASS:注解在class文件中可用,但會(huì)被VM丟棄
3)RUNTIME:VM將在運(yùn)行期也保留注解,因此可以通過(guò)反射機(jī)制讀取注解的信息
@Documented:將此注解包含在Javadoc中
@Inherited:允許子類(lèi)繼承父類(lèi)中的注解
大多數(shù)時(shí)候,程序員主要是定義自己的注解,并編寫(xiě)自己的處理器來(lái)處理它們.
UseCase.java
package com; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD)//用來(lái)定義你的注解將應(yīng)用在什么地方,本處應(yīng)用為方法 //用來(lái)定義該注解在哪一個(gè)級(jí)別可用,在源代碼中(source)類(lèi)文件中(class)或者運(yùn)行時(shí)(runtime) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { public int id(); public String description()default "no description"; } PasswordUtils .java package com; public class PasswordUtils { @UseCase(id=47,description="Passwords must contain at least one numeric") public boolean validatePassword(){ return true; } @UseCase(id=48) public String encryptPassword(String password){ return password; } @UseCase(id=49,description="Jong_Cai") public void showName(){ System.out.println("Jong_Cai"); } }
2.編寫(xiě)注解處理器
如果沒(méi)有用來(lái)讀取注解的工具,那注解也不會(huì)比注釋更有用.使用注解的過(guò)程中.很重要的一個(gè)部
分就是創(chuàng)建與使用注解處理器.Java SE5擴(kuò)展了反射機(jī)制的API,以幫助程序員構(gòu)造這類(lèi)工具.同時(shí),它還提供了一個(gè)外部工具apt幫助程序員解析帶有注解的Java源代碼.下面是一個(gè)非常簡(jiǎn)單的注解處理器,我們將用它來(lái)讀取PasswordUtils類(lèi),并使用反射機(jī)制查找@UseCase標(biāo)記.我們?yōu)槠涮峁┝艘唤Mid值得,然后它會(huì)列出在PasswordUtils中找到的用例,以及缺失的用例.
UseCaseTracker.java package com; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class UseCaseTracker { public static void trackUseCases(List<Integer> list, Class<?> cl) { for (Method m : cl.getDeclaredMethods()) { UseCase us = m.getAnnotation(UseCase.class); if (us != null) { System.out.println("Found Use Case:" + us.id() + " " + us.description()); list.remove(new Integer(us.id())); } } for (int i : list) { System.out.println("Warning:Missing use case-" + i); } } public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); Collections.addAll(list, 47,48,49,50,51); trackUseCases(list, PasswordUtils.class); } }
這個(gè)程序用到了兩個(gè)反射的方法:getDeclaredMethods()和getAnnotation(),它們都屬于AnnotatedElement接口(class,Method與Field等類(lèi)都實(shí)現(xiàn)了該接口).getAnnotation()方法返回指定類(lèi)型的注解對(duì)象,在這里就是UseCase,如果被注解的方法上沒(méi)有該類(lèi)型的注解,則返回null值.然后我們通過(guò)調(diào)用id()和description()方法從返回的UseCase對(duì)象中提取元素的值.其中encryptPassword()方法在注解的時(shí)候沒(méi)有指定description的值,因此處理器在處理它對(duì)應(yīng)的注解時(shí),通過(guò)description()方法取得的是默認(rèn)值no description.
Annotation在java的世界正鋪天蓋地展開(kāi),有空寫(xiě)這一篇簡(jiǎn)單的annotations的文章,算是關(guān)于Annotation入門(mén)的文章吧,希望能各位們能拋磚,共同學(xué)習(xí)......
不講廢話了,實(shí)踐才是硬道理.
3.實(shí)例
下面講一下annotation的概念先,再來(lái)講一下怎樣設(shè)計(jì)自己的annotation.
首先在jdk自帶的java.lang.annotation包里,打開(kāi)如下幾個(gè)源文件:
源文件Target.java
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
其中的@interface是一個(gè)關(guān)鍵字,在設(shè)計(jì)annotations的時(shí)候必須把一個(gè)類(lèi)型定義為@interface,而不能用class或interface關(guān)鍵字(會(huì)不會(huì)覺(jué)得sun有點(diǎn)吝嗇,偏偏搞得與interface這么像).
源文件Retention.java
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); } @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
看到這里,大家可能都模糊了,都不知道在說(shuō)什么,別急,往下看一下. 在上面的文件都用到了RetentionPolicy,ElementType這兩個(gè)字段,你可能就會(huì)猜到這是兩個(gè)java文件.的確,這兩個(gè)文件的源代碼如下:
源文件RetentionPolicy.java
public enum RetentionPolicy { SOURCE, CLASS, RUNTIME } public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
這是一個(gè)enum類(lèi)型,共有三個(gè)值,分別是SOURCE,CLASS 和 RUNTIME.
SOURCE代表的是這個(gè)Annotation類(lèi)型的信息只會(huì)保留在程序源碼里,源碼如果經(jīng)過(guò)了編譯之后,Annotation的數(shù)據(jù)就會(huì)消失,并不會(huì)保留在編譯好的.class文件里面。
ClASS的意思是這個(gè)Annotation類(lèi)型的信息保留在程序源碼里,同時(shí)也會(huì)保留在編譯好的.class文件里面,在執(zhí)行的時(shí)候,并不會(huì)把這一些信息加載到虛擬機(jī)(JVM)中去.注意一下,當(dāng)你沒(méi)有設(shè)定一個(gè)Annotation類(lèi)型的Retention值時(shí),系統(tǒng)默認(rèn)值是CLASS.
第三個(gè),是RUNTIME,表示在源碼、編譯好的.class文件中保留信息,在執(zhí)行的時(shí)候會(huì)把這一些信息加載到JVM中去的.
舉一個(gè)例子,如@Override里面的Retention設(shè)為SOURCE,編譯成功了就不要這一些檢查的信息;相反,@Deprecated里面的Retention設(shè)為RUNTIME,表示除了在編譯時(shí)會(huì)警告我們使用了哪個(gè)被Deprecated的方法,在執(zhí)行的時(shí)候也可以查出該方法是否被Deprecated.
源文件ElementType.java
public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE } public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE }
@Target里面的ElementType是用來(lái)指定Annotation類(lèi)型可以用在哪一些元素上的.說(shuō)明一下:TYPE(類(lèi)型), FIELD(屬性), METHOD(方法), PARAMETER(參數(shù)), CONSTRUCTOR(構(gòu)造函數(shù)),LOCAL_VARIABLE(局部變量), ANNOTATION_TYPE,PACKAGE(包),其中的TYPE(類(lèi)型)是指可以用在Class,Interface,Enum和Annotation類(lèi)型上.
另外,從1的源代碼可以看出,@Target自己也用了自己來(lái)聲明自己,只能用在ANNOTATION_TYPE之上.
如果一個(gè)Annotation類(lèi)型沒(méi)有指明@Target使用在哪些元素上,那么它可以使用在任何元素之上,這里的元素指的是上面的八種類(lèi)型.
舉幾個(gè)正確的例子:
@Target(ElementType.METHOD)
@Target(value=ElementType.METHOD)
@Target(ElementType.METHOD,ElementType.CONSTRUCTOR)
具體參考一下javadoc文檔
源文件它們都使用了@Documented,@Documented的目的就是讓這一個(gè)Annotation類(lèi)型的信息能夠顯示在javaAPI說(shuō)明文檔上;沒(méi)有添加的話,使用javadoc生成API文檔的時(shí)候就會(huì)找不到這一個(gè)類(lèi)型生成的信息.
另外一點(diǎn),如果需要把Annotation的數(shù)據(jù)繼承給子類(lèi),那么就會(huì)用到@Inherited這一個(gè)Annotation類(lèi)型.
下面講的設(shè)計(jì)一個(gè)最簡(jiǎn)單的Annotation例子,這一例子共用四個(gè)文件;
Description.java
package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Description { String value(); } package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Description { String value(); }
說(shuō)明:所有的Annotation會(huì)自動(dòng)繼承java.lang.annotation這一個(gè)接口,所以不能再去繼承別的類(lèi)或是接口.
最重要的一點(diǎn),Annotation類(lèi)型里面的參數(shù)該怎么設(shè)定:
第一,只能用public或默認(rèn)(default)這兩個(gè)訪問(wèn)權(quán)修飾.例如,String value();這里把方法設(shè)為defaul默認(rèn)類(lèi)型.
第二,參數(shù)成員只能用基本類(lèi)型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類(lèi)型和String,Enum,Class,annotations等數(shù)據(jù)類(lèi)型,以及這一些類(lèi)型的數(shù)組.例如,String value();這里的參數(shù)成員就為String.
第三,如果只有一個(gè)參數(shù)成員,最好把參數(shù)名稱(chēng)設(shè)為"value",后加小括號(hào).例:上面的例子就只有一個(gè)參數(shù)成員.
Name.java
package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //注意這里的@Target與@Description里的不同,參數(shù)成員也不同 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Name { String originate(); String community(); } package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //注意這里的@Target與@Description里的不同,參數(shù)成員也不同 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Name { String originate(); String community(); }
JavaEyer.java
package lighter.javaeye.com; @Description("javaeye,做最棒的軟件開(kāi)發(fā)交流社區(qū)") public class JavaEyer { @Name(originate="創(chuàng)始人:robbin",community="javaEye") public String getName() { return null; } @Name(originate="創(chuàng)始人:江南白衣",community="springside") public String getName2() { return "借用兩位的id一用,寫(xiě)這一個(gè)例子,請(qǐng)見(jiàn)諒!"; } } package lighter.javaeye.com; @Description("javaeye,做最棒的軟件開(kāi)發(fā)交流社區(qū)") public class JavaEyer { @Name(originate="創(chuàng)始人:robbin",community="javaEye") public String getName() { return null; } @Name(originate="創(chuàng)始人:江南白衣",community="springside") public String getName2() { return "借用兩位的id一用,寫(xiě)這一個(gè)例子,請(qǐng)見(jiàn)諒!"; } }
寫(xiě)一個(gè)可以運(yùn)行提取JavaEyer信息的類(lèi)TestAnnotation
package lighter.javaeye.com; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; public class TestAnnotation { /** * author lighter * 說(shuō)明:具體關(guān)天Annotation的API的用法請(qǐng)參見(jiàn)javaDoc文檔 */ public static void main(String[] args) throws Exception { String CLASS_NAME = "lighter.javaeye.com.JavaEyer"; Class test = Class.forName(CLASS_NAME); Method[] method = test.getMethods(); boolean flag = test.isAnnotationPresent(Description.class); if(flag) { Description des = (Description)test.getAnnotation(Description.class); System.out.println("描述:"+des.value()); System.out.println("-----------------"); } //把JavaEyer這一類(lèi)有利用到@Name的全部方法保存到Set中去 Set<Method> set = new HashSet<Method>(); for(int i=0;i<method.length;i++) { boolean otherFlag = method[i].isAnnotationPresent(Name.class); if(otherFlag) set.add(method[i]); } for(Method m: set) { Name name = m.getAnnotation(Name.class); System.out.println(name.originate()); System.out.println("創(chuàng)建的社區(qū):"+name.community()); } } } package lighter.javaeye.com; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; public class TestAnnotation { /** * author lighter * 說(shuō)明:具體關(guān)天Annotation的API的用法請(qǐng)參見(jiàn)javaDoc文檔 */ public static void main(String[] args) throws Exception { String CLASS_NAME = "lighter.javaeye.com.JavaEyer"; Class test = Class.forName(CLASS_NAME); Method[] method = test.getMethods(); boolean flag = test.isAnnotationPresent(Description.class); if(flag) { Description des = (Description)test.getAnnotation(Description.class); System.out.println("描述:"+des.value()); System.out.println("-----------------"); } //把JavaEyer這一類(lèi)有利用到@Name的全部方法保存到Set中去 Set<Method> set = new HashSet<Method>(); for(int i=0;i<method.length;i++) { boolean otherFlag = method[i].isAnnotationPresent(Name.class); if(otherFlag) set.add(method[i]); } for(Method m: set) { Name name = m.getAnnotation(Name.class); System.out.println(name.originate()); System.out.println("創(chuàng)建的社區(qū):"+name.community()); } } }
運(yùn)行結(jié)果:
描述:javaeye,做最棒的軟件開(kāi)發(fā)交流社區(qū) 創(chuàng)始人:robbin 創(chuàng)建的社區(qū):javaEye 創(chuàng)始人:江南白衣 創(chuàng)建的社區(qū):springside
相關(guān)文章
Java線性結(jié)構(gòu)中棧、隊(duì)列和串的基本概念和特點(diǎn)詳解
前幾天小編給大家介紹了Java線性結(jié)構(gòu)中的鏈表,除了鏈表這種結(jié)構(gòu)之外,實(shí)際上還有棧、隊(duì)列、串等結(jié)構(gòu),那么這些結(jié)構(gòu)又有哪些特點(diǎn)呢,本文就給大家詳細(xì)的介紹一下,感興趣的小伙伴跟著小編一起來(lái)看看吧2023-07-07解決@FeignClient注入service失敗問(wèn)題
這篇文章主要介紹了解決@FeignClient注入service失敗問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Java實(shí)現(xiàn)級(jí)聯(lián)下拉結(jié)構(gòu)的示例代碼
在開(kāi)發(fā)過(guò)程中,會(huì)遇到很多的實(shí)體需要將查出的數(shù)據(jù)處理為下拉或者級(jí)聯(lián)下拉的結(jié)構(gòu),提供給前端進(jìn)行展示。本文為大家介紹了java封裝下拉和級(jí)聯(lián)下拉的通用工具類(lèi),需要的可以參考一下2022-06-06JAVA實(shí)現(xiàn)FTP斷點(diǎn)上傳的方法
這篇文章主要介紹了JAVA實(shí)現(xiàn)FTP斷點(diǎn)上傳的方法,涉及java使用FTP實(shí)現(xiàn)文件傳輸?shù)南嚓P(guān)技巧,需要的朋友可以參考下2015-06-06springboot與vue詳解實(shí)現(xiàn)短信發(fā)送流程
隨著人工智能的不斷發(fā)展,機(jī)器學(xué)習(xí)這門(mén)技術(shù)也越來(lái)越重要,很多人都開(kāi)啟了學(xué)習(xí)機(jī)器學(xué)習(xí),本文就介紹了機(jī)器學(xué)習(xí)的基礎(chǔ)內(nèi)容2022-06-06Redisson分布式閉鎖RCountDownLatch的使用詳細(xì)講解
分布式鎖和我們java基礎(chǔ)中學(xué)習(xí)到的synchronized略有不同,synchronized中我們的鎖是個(gè)對(duì)象,當(dāng)前系統(tǒng)部署在不同的服務(wù)實(shí)例上,單純使用synchronized或者lock已經(jīng)無(wú)法滿足對(duì)庫(kù)存一致性的判斷。本次主要講解基于rediss實(shí)現(xiàn)的分布式鎖2023-02-02Java8語(yǔ)法糖之Lambda表達(dá)式的深入講解
這篇文章主要給大家介紹了關(guān)于Java8語(yǔ)法糖之Lambda表達(dá)式的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02詳談Spring是否支持對(duì)靜態(tài)方法進(jìn)行Aop增強(qiáng)
這篇文章主要介紹了Spring是否支持對(duì)靜態(tài)方法進(jìn)行Aop增強(qiáng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12java實(shí)現(xiàn)文件打包壓縮輸出到瀏覽器下載
這篇文章主要介紹了java實(shí)現(xiàn)文件打包壓縮輸出到瀏覽器下載,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11