Java注解(Annotation)詳解
1.Annotation的概念
An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.
- 注解是一種可以添加到Java源代碼的元數(shù)據(jù).
- 類,方法,變量,參數(shù),包都可以被注解.
- 注解對注解的代碼并沒有直接的影響.
- 注解僅僅是個(gè)標(biāo)記.注解之所以起作用是對其解析后做了相應(yīng)的處理
2.Annotation分類
- 標(biāo)準(zhǔn)Annotation
- 標(biāo)準(zhǔn)Annotation是指Java內(nèi)置的三個(gè)Annnotaion:
- @Override:用于修飾此方法覆蓋了父類的方法.
- @Deprecated:用于修飾已經(jīng)過時(shí)的方法.
- @SuppressWarnnings:用于通知java編譯器禁止特定的編譯警告.
- 元Annotation(注解的注解)
- 元Annotation是用來定義Annotation的Annotation
- 元Annotation可以定義Annotation的作用范圍,使用在什么元素上等
- 元注解共有四種@Retention, @Target, @Inherited, @Documented
- 自定義Annotation
3.元Annotation
- @Retention:注在其他的注解A上,用來說明A的保留范圍,可選值 SOURCE(源碼時(shí))稚照,CLASS(編譯時(shí))撵颊,RUNTIME(運(yùn)行時(shí))彬坏,默認(rèn)為 CLASS
- SOURCE:A只保留在源碼中,A會(huì)被編譯期忽略.(源碼可用)
- CLASS:A會(huì)通過編譯保存在CLASS文件中,但會(huì)被JVM在運(yùn)行時(shí)忽略,運(yùn)行時(shí)不可見.(源碼+CLASS可用)
- RUNTIME:A會(huì)被JVM獲取,并在運(yùn)行時(shí)通過反射獲取.(源碼+CLASS+運(yùn)行時(shí)均可用)
- @Target:注在其他的注解A上,用來限制A可用修飾那些程序元素.未標(biāo)注Target表示無限制,可修飾所有元素.
- ANNOTATION_TYPE: A可以應(yīng)用到其他注解上
- CONSTRUCTOR: A可以使用到構(gòu)造器上
- FIELD: A可以使用到域或?qū)傩陨?/li>
- LOCAL_VARIABLE: A可以使用到局部變量上。
- METHOD: A可以使用到方法上冒黑。
- PACKAGE: A可以使用到包聲明上。
- PARAMETER: A可以使用到方法的參數(shù)上
- TYPE: A可以使用到類,接口(包括注解),或枚舉的聲明上
- @Inherited:默認(rèn)情況下,父類的注解不會(huì)被子類繼承.
- Inherited注在其他的注解A上.
- 只有當(dāng)A是注解在類Class上面,Inherited才會(huì)起作用,其他任何情況下無效果.
- 當(dāng)A注解在類C上面,則C的所有子孫類,都會(huì)繼承應(yīng)用A注解;
- @Documented:注在其他的注解A上,A將會(huì)作為Javadoc產(chǎn)生的文檔中的內(nèi)容疙咸。注解都默認(rèn)不會(huì)成為成為文檔中的內(nèi)容尤蛮。
4.自定義Annotation
-
創(chuàng)建自定義Annotation流程
- public @interface 自定義注解名稱
public @interface CustomAnnotation{***}
- 設(shè)置自定義Annotation的保留范圍和目標(biāo),Retention和Target是最重要的兩個(gè)元Anitation.
@Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.TYPE ) public @interface CustomAnnotation{***}
- 設(shè)置自定義Annotation的注解參數(shù)(注解成員)
- 注解參數(shù)支持的數(shù)據(jù)類型
- 所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
- String類型
- Class類型
- enum類型
- Annotation類型
- 以上所有類型的一維數(shù)組
- 注解參數(shù)聲明方式
@Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.TYPE ) public @interface CustomAnnotation{ //注解參數(shù)類型可以是1-6中任一種,包括枚舉 public enum Skill{JAVA,ANDROID,IOS} Skill mySkill() default Skill.ANDROID; String attr1(); //可以使用default設(shè)置默認(rèn)值 int attr2() default 100; //修飾符只能用public public boolean attr3() default false; } @Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.TYPE ) public @interface CustomAnnotation{ //只有一個(gè)注解參數(shù),使用value() String value(); }
- 自定義Annotation的參數(shù)類型必須滿足上一條1到6中的范圍.
- 自定義Annotation的參數(shù)訪問方法只能是public,或不寫.
- 自定義Annotation的參數(shù)可以加 default 設(shè)置默認(rèn)值.
- 自定義Annotation若只有1個(gè)參數(shù),使用value().
- 注解參數(shù)支持的數(shù)據(jù)類型
- public @interface 自定義注解名稱
-
自定義Annotation的注解參數(shù)的默認(rèn)值
注解元素必須有確定的值媳友,要么在定義注解的默認(rèn)值中指定,要么在使用注解時(shí)指定抵屿,非基本類型的注解元素的值不可為null庆锦。因此, 使用空字符串或0作為默認(rèn)值是一種常用的做法捅位。這個(gè)約束使得處理器很難表現(xiàn)一個(gè)元素的存在或缺失的狀態(tài)轧葛,因?yàn)槊總€(gè)注解的聲明中,所有元素都存在艇搀,并且都具有相應(yīng)的值尿扯,為了繞開這個(gè)約束,我們只能定義一些特殊的值焰雕,例如空字符串或者負(fù)數(shù)衷笋,一次表示某個(gè)元素不存在,在定義注解時(shí)矩屁,這已經(jīng)成為一個(gè)習(xí)慣用法辟宗。
示例: @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface AnotherAnnotation{ String author() default ""; int age() default -1; }
-
使用剛剛創(chuàng)建的自定義注解
@CustomAnnotation(attr1 = "屬性1", attr2 = 90, attr3 = true) public class AnnotationTestClass{ *** }
5.Annotation解析
- 運(yùn)行時(shí) Annotation 解析
運(yùn)行時(shí) Annotation 指 @Retention 為 RUNTIME 的 Annotation
- Class,Method,Field中都有以下3個(gè)方法可以調(diào)用
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 按照傳入的參數(shù)獲取指定類型的注解。返回null說明當(dāng)前元素不帶有此注解吝秕。
- public final boolean isAnnotationPresent(Class<? extends Annotation> annotationType) 檢查傳入的注解是否存在于當(dāng)前元素泊脐。
- public Annotation[] getAnnotations() 返回該元素的所有注解,包括沒有顯式定義該元素上的注解烁峭。
- 運(yùn)行時(shí) Annotation 解析示例
public void testCustomAnnotation() { try { Class cls = Class.forName("com.jet.annotation.AnnotationTestClass"); CustomAnnotation customAnnotation = (CustomAnnotation)cls.getAnnotation(CustomAnnotation.class); System.out.println("customAnnotation mySkill:" + cus.mySkill()); System.out.println("customAnnotation attr1:" + cus.attr1()); System.out.println("customAnnotation attr2:" + cus.attr2()); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
- 編譯時(shí) Annotation 解析
編譯時(shí) Annotation 指 @Retention 為 CLASS 的 Annotation容客,甴編譯器自動(dòng)解析
6.編譯時(shí)Annotation解析
編譯時(shí)Annotation解析 相對復(fù)雜,下面單獨(dú)進(jìn)行分析
首先申明:下面內(nèi)容僅僅討論 編譯時(shí)Annotation的解析
- 編譯時(shí)Annotation的解析,是由Annotation Processor完成
- Annotation Processor(注解處理器)
- 注解處理器是一個(gè)在javac中的,用來在編譯時(shí)掃描和處理注解的工具
- 我們可以為特定的注解,注冊自定義的注解處理器
- 在編譯期間,JVM會(huì)自動(dòng)運(yùn)行注冊過的注解處理器
- 一個(gè)注解的Annotation Processor,以Java代碼(或者編譯過的class)為輸入,生成.java文件作為輸出.這意味著我們可以生成新的Java代碼!這些生成的Java代碼是在生成的.java文件中,新生成的.java文件會(huì)和普通的手動(dòng)編寫的Java源代碼一樣被javac編譯
- 每一個(gè)注解處理器都是繼承于AbstractProcessor,需要關(guān)注的有以下4個(gè)方法
public abstract class AbstractProcessor implements Processor {
//對一些工具進(jìn)行初始化
public synchronized void init(ProcessingEnvironment processingEnv)
//你在這里定義你的注解處理器注冊到哪些注解上,必須指定;
//它的返回值是一個(gè)字符串的集合,包含本處理器想要處理的注解類型的合法全稱
public Set<String> getSupportedAnnotationTypes()
//指定該注解處理器使用的JAVA版本,通常返回SourceVersion.latestSupported()
public SourceVersion getSupportedSourceVersion()
//真正生成java代碼的地方
//annotations:請求處理的注解類型集合
//roundEnv:可以讓你查詢出包含特定注解的被注解元素约郁,相當(dāng)于“有關(guān)全局源碼的上下文環(huán)境”
//如果返回 true缩挑,則這些注解已聲明并且不要求后續(xù) Processor 處理它們;
//如果返回 false鬓梅,則這些注解未聲明并且可能要求后續(xù) Processor 處理它們
public abstract boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv)
}
- 自定義注解處理器,就是繼承AbstractProcessor并重寫上述4個(gè)方法
關(guān)于編譯時(shí)Annotation解析,這里推薦一篇文章【Android】注解框架(三)-- 編譯時(shí)注解供置,手寫B(tài)utterKnife,按照文章上面流程敲一遍代碼,相信可以對自定義注解的創(chuàng)建及解析有一個(gè)深入的了解!