第三節(jié) 自定義Annotation
使用過spring框架,ejb框架,cxf,junit2等支持Annotation技術(shù)的框架的人都會很清楚,其中除了jdk自帶的一些Annotation以外,我們還可以自己定義很多Annotation幫助我們進行框架的搭建,這一小節(jié)我們將逐步的進行自定義Annotation的說明。
Ok,開始我們的第一個Annotation程序之旅吧。
一:Quick Start
定義Annotation的時候和定義接口的方式很類似,只不過再interface前面加了@,我感覺在這一點sun公司確實有一些吝嗇,為何不開辟一個新的類型標識符呢,呵呵,當(dāng)然這樣的抱怨也是無關(guān)緊要的,只要能達到目的即可。牢騷也不不多發(fā)了,直接上codes。
- package com.wangwenjun.annatation.userdefined;
-
- public @interface UserdefinedAnnotation {
-
- }
代碼很簡單,沒有任何的結(jié)構(gòu),只是一個空的Annotation,接下來演示一下如何對其進行使用。
- package com.wangwenjun.annatation.userdefined;
-
- public class UseAnnotation {
-
- @UserdefinedAnnotation
- public static void main(String[] args) {
- System.out.println("hello");
- }
- }
上述的代碼簡單到再也不能簡單,甚至簡單到說明不了任何問題,只是從它上面可以看得出來我們?nèi)绾巫远ㄒ粋€Annotation,并且如何的使用它,我們并沒有給自定義Annotation中添加任何的屬性,甚至沒有指定其保持力(Retention),可繼承性(Inherited),標注對象(Target)等,再接下來的描述中我們會逐步進行說明。
二:Annotation屬性值
Annotation屬性值大致有以下三種:
● 基本類型
● 數(shù)組類型
● 枚舉類型
我們在下面的文字中將會一個個的進行演示和說明。
1:基本串類型
- package com.wangwenjun.annatation.userdefined;
-
- public @interface UserdefinedAnnotation {
- int value();
- String name();
- String address();
- }
上面是一個自定義的annotation,可能稍微有一些復(fù)雜,只不過是為了更多的說明一下問題,可以看出來在定義屬性的時候有點像interface定義方法一樣,每一個屬性名稱之后需要加上括號,接下來看看如何使用。
- package com.wangwenjun.annatation.userdefined;
- public class UseAnnotation {
-
- @UserdefinedAnnotation(value=123,name="wangwenjun",address="火星")
- public static void main(String[] args) {
- System.out.println("hello");
- }
- }
如果在使用UserdefinedAnnotation時候不給相關(guān)的屬性賦值,會出現(xiàn)錯誤。
需要說明的一點事如果一個annotation中只有一個屬性名字叫value,我沒在使用的時候可以給出屬性名也可以省略。
- public @interface UserdefinedAnnotation {
- int value();
- }
- package com.wangwenjun.annatation.userdefined;
-
- public class UseAnnotation {
-
- @UserdefinedAnnotation(value=123)
- public static void main(String[] args) {
- System.out.println("hello");
- }
- }
也可以寫成如下的形式
- @UserdefinedAnnotation(123)
- public static void main(String[] args) {
- System.out.println("hello");
- }
直接對其進行了省略。如果定義的屬性名字不叫value,那么屬性名字是不可以省略的哦!那是因為value屬性名是annotation默認的一個屬性名,這一點很重要的哦,千萬不能忽略。
2:數(shù)組類型
我們在自定義annotation中定義一個數(shù)組類型的屬性,代碼如下:
- public @interface UserdefinedAnnotation {
- int[] value();
- }
我們?nèi)绾问褂媚兀a如下:
- public class UseAnnotation {
-
- @UserdefinedAnnotation({123})
- public static void main(String[] args) {
- System.out.println("hello");
- }
- }
注意1:其中123外面的大括號是可以被省略的,因為只有一個元素,如果里面有一個以上的元素的話,花括號是不能被省略的哦。比如{123,234}。
注意2:其中屬性名value我們在使用的時候進行了省略,那是因為他叫value,如果是其他名字我們就不可以進行省略了必須是@UserdefinedAnnotation(屬性名={123,234})這樣的格式。
3:枚舉類型
自從jdk5.0以后,java引進了枚舉類型,我個人比較喜歡這樣的方式尤其在進行業(yè)務(wù)邏輯判斷的if或者switch子句中使用很方便,而且還不容易出錯,大多時候都是作為一個函數(shù)的形式參數(shù)而存在,關(guān)于枚舉類型的使用請查看相應(yīng)的doc文檔。
我們定義一個enum類型
- package com.wangwenjun.annatation.userdefined;
-
- public enum DateEnum {
- Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
- }
- 然后在定義一個annotation
- package com.wangwenjun.annatation.userdefined;
-
- public @interface UserdefinedAnnotation {
- DateEnum week();
- }
可以看出annotation中的屬性類型為enum類型的,接下來我們看看他如何來使用
- package com.wangwenjun.annatation.userdefined;
-
- public class UseAnnotation {
-
- @UserdefinedAnnotation(week=DateEnum.Sunday)
- public static void main(String[] args) {
- System.out.println("hello");
- }
- }
在使用上也是很方便的,直接用來進行相關(guān)的引用,這樣再應(yīng)用的過程中就不會出現(xiàn)錯誤。
4:默認值
有時候我們在使用annotation的時候某一些屬性值是會被經(jīng)常使用到的,或者說他會有一個默認值給我們直接進行使用,那么我們在定義annotation的時候就可以為屬性直接給出默認值,下面進行一下簡單的示例。
- public @interface UserdefinedAnnotation {
- String name() default "zhangsan";
- }
在使用的時候我們可以不進行指定
- public class UseAnnotation {
-
- @UserdefinedAnnotation()
- public static void main(String[] args) {
- System.out.println("hello");
- }
- }
當(dāng)然我們也可以自己對其進行重新的設(shè)置,其中數(shù)組和枚舉類型的默認值基本上類似,就不多做贅述了,自己進行測試即可。
5:注意
● Annotation是不可以繼承其他接口的,這一點是需要進行注意,這也是annotation的一個規(guī)定吧。
● Annotation也是存在包結(jié)構(gòu)的,在使用的時候直接進行導(dǎo)入即可。
● Annotation類型的類型只支持原聲數(shù)據(jù)類型,枚舉類型和Class類型的一維數(shù)組,其他的類型或者用戶自定義的類都是不可以作為annotation的類型,我查看過文檔并且進行過測試。
三:Retention標記
Retention標記是告知編譯器如何來處理我們自定義的annotation,指示注釋類型的注釋要保留多久。如果注釋類型聲明中不存在 Retention 注釋,則保留策略默認為 RetentionPolicy.CLASS。
查看他的源代碼,會發(fā)現(xiàn)他有一個屬性value,類型為RetentionPolicy,RetentionPolicy是一個枚舉類型。其中有三個類型的值分別代表不同的意思。
CLASS 編譯器將把注釋記錄在類文件中,但在運行時 VM 不需要保留注釋。
RUNTIME 編譯器將把注釋記錄在類文件中,在運行時 VM 將保留注釋,因此可以反射性地讀取。
SOURCE 編譯器要丟棄的注釋
下面是一段簡短的定義代碼示例。
- package com.wangwenjun.annatation.userdefined;
-
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
-
- @Retention(RetentionPolicy.RUNTIME)
- public @interface UserdefinedAnnotation {
- String name() default "zhangsan";
- }
從上面的實例中我們自定義的annotation是一個runntime范圍的annotation,也就是說他會保持在源文件中并且也會在運行時由JVM自動調(diào)用。
我們先對它有一個感性的認識,知道有這么一個東西,在后面的文章中我會以一個示例對其進行詳細的說明(會涉及到反射的相關(guān)東西)。其實Retention的名字翻譯過來就是“保持力”的意思,說明的很清楚,就是我的annotation存放在哪些地方,也就是說我的annotation他的影響力到底在哪。
四:AnnotatedElement
在jdk5.0以后java反射包增加了這樣一個接口,主要是用來對annotation進行操作的,其中AccessibleObject, Class, Constructor, Field, Method, Package都對其進行了實現(xiàn)繼承??偣灿幸韵滤膫€方法:
<T extends Annotation>
T
getAnnotation(Class<T> annotationType)
如果存在該元素的指定類型的注釋,則返回這些注釋,否則返回 null。
Annotation[]
getAnnotations()
返回此元素上存在的所有注釋。
Annotation[]
getDeclaredAnnotations()
返回直接存在于此元素上的所有注釋。
boolean isAnnotationPresent(Class<? extends Annotation> annotationType)
如果指定類型的注釋存在于此元素上,則返回 true,否則返回 false。
在接下來的章節(jié)中我們會進行說明。
五:Target 標記
在我們之前的實例中我們定義的annotation可以放在一個類的任何位置,那么我們是否可以對annotation的位置進行設(shè)置呢,答案是可以的,這就是我們所要說的的Target 標記,他里面也有一個枚舉類型的屬性value,其中枚舉類型為ElementType,有很多自定義的屬性,如下所示:
ANNOTATION_TYPE
注釋類型聲明
CONSTRUCTOR
構(gòu)造方法聲明
FIELD
字段聲明(包括枚舉常量)
LOCAL_VARIABLE
局部變量聲明
METHOD
方法聲明
PACKAGE
包聲明
PARAMETER
參數(shù)聲明
TYPE
類、接口(包括注釋類型)或枚舉聲明
上面的列表已經(jīng)講述的很清楚了,我們就不再進行說明了,直接來一個小程序進行演示吧,還是先來一個annotation,假設(shè)我們的annotation只能放在方法的前面
- package com.wangwenjun.annatation.userdefined;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Target;
-
- @Target(ElementType.METHOD)
- public @interface UserdefinedAnnotation {
- String name() default "zhangsan";
- }
- package com.wangwenjun.annatation.userdefined;
- public class UseAnnotation {
-
- @UserdefinedAnnotation()
- public static void main(String[] args) {
- System.out.println("hello");
- }
- }
我們的annotation只能放在main方法上面放在其他的位置會出現(xiàn)錯誤。
六:Documented 標記
這個annotation非常簡單,也非常容易理解,使用過javadoc命令的人都會很清楚,我們可以用javadoc命令將方法,類,變量等前面的注釋轉(zhuǎn)換成文檔,在默認的情況下javadoc命令不會將我們的annotation生成再doc中去的,所以使用該標記就是告訴jdk讓它也將annotation生成到doc中去,比如:
- package com.wangwenjun.annatation.userdefined;
-
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Target;
-
- @Target(ElementType.METHOD)
- @Documented
- public @interface UserdefinedAnnotation {
- String name() default "zhangsan";
- }
七:Inherited標記
該標記的意思就是說,比如有一個類A,在他上面有一個標記annotation,那么A的子類B是否不用再次標記annotation就可以繼承得到呢?答案是肯定的,我們做一個簡單的演示,首先我們有一個annotation
- package com.wangwenjun.annatation.userdefined;
-
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Inherited;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Documented
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- public @interface UserdefinedAnnotation {
- String name() default "zhangsan";
- }
接著定義一個父類
package com.wangwenjun.annatation.userdefined;
@UserdefinedAnnotation
public class ParentClass {
}
父類什么都沒有干,只是一個空的類,并且有UserdefinedAnnotation的標記,然后我們寫一個繼承他的子類。
- package com.wangwenjun.annatation.userdefined;
-
- public class ChildClass extends ParentClass{
-
- }
我們準備工作都已經(jīng)做完了,現(xiàn)在就是利用反射機制進行一下簡短的測試
- package com.wangwenjun.annatation.userdefined;
-
- public class TestInherited {
- public static void main(String[] args) {
- Class<ChildClass> clazz = ChildClass.class;
- boolean isExist=clazz.isAnnotationPresent(UserdefinedAnnotation.class);
- if(isExist){
- System.out.println("子類繼承了父類的annotation");
- }else{
- System.out.println("子類沒有繼承父類的annotation");
- }
- }
- }
打印結(jié)果為:子類繼承了父類的annotation??梢钥吹阶宇惞焕^承了父類的annotation標記。