Java自定義注解詳解+實(shí)例

在很多框架中都使用了自定義注解,之前在項(xiàng)目中也用到了自定義注解來對代碼做一些解耦,今天就給大家介紹下自定義注解的使用方法,對于自定義注解其實(shí)很簡單,大家只要搞清楚如何定義自定義注解和如何獲取定義的自定義注解內(nèi)容就基本掌握了自定義注解,后續(xù)就可以在自己的項(xiàng)目中去使用自定義注解完成一些功能。

1. 元注解

JDK 1.5開始jdk就定義了元注解,用來定義其他的自定義注解,目前提供的元注解主要有4個(gè):

  • @Target
  • @Retention
  • @Documented
  • .@Inherited

@Target

@Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))。

取值(ElementType)有:

  • 1.CONSTRUCTOR:用于描述構(gòu)造器
  • 2.FIELD:用于描述域
  • 3.LOCAL_VARIABLE:用于描述局部變量
  • 4.METHOD:用于描述方法
  • 5.PACKAGE:用于描述包
  • 6.PARAMETER:用于描述參數(shù)
  • 7.TYPE:用于描述類、接口(包括注解類型) 或enum聲明

我們在日常自定義注解中常用的類型有FIELD、METHOD、PARAMETER、TYPE。

@Retention

@Retention定義了該Annotation被保留的時(shí)間長短。
  
取值(RetentionPoicy)有:

  • 1.SOURCE:在源文件中有效(即源文件保留)
  • 2.CLASS:在class文件中有效(即class保留)
  • 3.RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)

我們在自定義注解用用的比較多的自然是RUNTIME了,這樣保證注解在運(yùn)行時(shí)是有效的,我們在其他框架中遇到的也大部分都是RUNTIME的。

@Documented

@Documented用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個(gè)標(biāo)記注解,沒有成員。

@Inherited

@Inherited 元注解是一個(gè)標(biāo)記注解,@Inherited闡述了某個(gè)被標(biāo)注的類型是被繼承的。如果一個(gè)使用了@Inherited修飾的annotation類型被用于一個(gè)class,則這個(gè)annotation將被用于該class的子類。

2.自定義注解

使用@interface自定義注解時(shí),自動(dòng)繼承了java.lang.annotation.Annotation接口,由編譯程序自動(dòng)完成其他細(xì)節(jié)。在定義注解時(shí),不能繼承其他的注解或接口。@interface用來聲明一個(gè)注解,其中的每一個(gè)方法實(shí)際上是聲明了一個(gè)配置參數(shù)。方法的名稱就是參數(shù)的名稱,返回值類型就是參數(shù)的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數(shù)的默認(rèn)值。

注解參數(shù)支持的類型如下:

  • 1.所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
  • 2.String類型
  • 3.Class類型
  • 4.enum類型
  • 5.Annotation類型
  • 6.以上所有類型的數(shù)組

下面我們寫個(gè)例子,定義幾個(gè)自定義注解,并獲取這些自定義注解中的值。

定義了一個(gè)可以作用于類、接口、枚舉上的注解MyType。


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

import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 16:10 17/9/22.
 * @Modify by:
 */
@Documented
@Target({ ElementType.TYPE})
@Retention(RUNTIME)
public @interface MyType {

    String value() default "";
    String className() default "";
}

定義了一個(gè)可以作用于類的方法上的注解MyMethod。

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

import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 16:12 17/9/22.
 * @Modify by:
 */
@Documented
@Target({ ElementType.METHOD})
@Retention(RUNTIME)
public @interface MyMethod {
    String value() default "";
    String methodName() default "";

}

定義了一個(gè)可以作用于類內(nèi)部變量的注解MyField。


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

import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 16:07 17/9/22.
 * @Modify by:
 */
@Documented
@Target({ ElementType.FIELD})
@Retention(RUNTIME)
public @interface MyField {

    String value() default "";
    String name() default "";
    String type() default"String";

}

下面我們寫個(gè)例子,定義一個(gè)類使用這些注解。

import com.monkey01.annotation.MyField;
import com.monkey01.annotation.MyMethod;
import com.monkey01.annotation.MyType;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 16:14 17/9/22.
 * @Modify by:
 */
@MyType(value = "test", className = "TestClass")
public class TestClass {

    @MyField(value = "testNum", name="num", type = "int")
    private int num;

    @MyField(value = "testName", name="name", type = "String")
    private String name;

    @MyMethod(value = "print ")
    public String print(){
        return "hello annotation";
    }
}

從例子中可以看到,在類名上面使用了MyType的注解,在內(nèi)部申明的變量上都定義了MyField,在內(nèi)部方法上使用了MyMethod注解,java也能自動(dòng)識(shí)別這些自定義注解。

寫好了測試類后,我們下一步要做的就是如何獲取這些自定義注解上定義的屬性值,因?yàn)槎x自定義注解的目的是為了將一些公共的功能,能夠比較優(yōu)雅的與實(shí)際業(yè)務(wù)代碼隔離開。獲取注解上的屬性使用的方法實(shí)際上就是利用反射來獲取其中的注解。

下面我們寫一個(gè)類來實(shí)現(xiàn)讀取注解屬性并打印出來。

import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 16:14 17/9/22.
 * @Modify by:
 */
public class PrintAnnotation {

    private String allAnnotation = new String();

    public String printAllAnnotation(){
        Set<Class<?>> clazzes = new Reflections("com.monkey01").getTypesAnnotatedWith(MyType.class);

        for (Class<?> clazz : clazzes) {
            printMyType(clazz);
        }
        System.out.println(allAnnotation);
        return allAnnotation;
    }

    private void printMyType(Class<?> clazz) {
        MyType myType = clazz.getAnnotation(MyType.class);
        allAnnotation = allAnnotation + clazz.getName() + ": " + myType.value() + "-" + myType.className() + "\n";
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields){
            MyField myField = field.getAnnotation(MyField.class);
            if(myField != null) {
                allAnnotation = allAnnotation + myField.value() + "-" + myField.name() + "-" + myField.type() + "\n";
            }
        }
        Method[] methods = clazz.getMethods();
        for(Method method : methods){

            MyMethod myMethod = method.getAnnotation(MyMethod.class);
            if(myMethod != null) {
                allAnnotation = allAnnotation + myMethod.methodName() + "-" + myMethod.value() + "\n";
            }
        }

    }

}

從代碼可以看到首先通過反射來獲取com.monkey01包下的使用了MyType注解的類。

        Set<Class<?>> clazzes = new Reflections("com.monkey01").getTypesAnnotatedWith(MyType.class);

再循環(huán)取出使用了MyType注解的類,并打印其中的FIELD和METHOD,同樣是使用反射獲取MyField和MyMethod注解。

要獲取一個(gè)注解定義的屬性值,需要先獲取到這個(gè)注解類對象,所有的注解類對象獲取方法都是一樣的,調(diào)用class的getAnnotation方法,入?yún)檫@個(gè)注解類。

public <A extends Annotation> A getAnnotation(Class<A> annotationClass) 
MyType myType = clazz.getAnnotation(MyType.class);

獲取到注解類對象后就可以調(diào)用定義的方法,獲取這些方法屬性值了,是不是很簡單。

        allAnnotation = allAnnotation + clazz.getName() + ": " + myType.value() + "-" + myType.className() + "\n";

要獲取Field和Method的屬性需要先通過反射獲取到獲取這個(gè)類的Field和Method。

Field[] fields = clazz.getDeclaredFields();

Method[] methods = clazz.getMethods();

對于MyField和MyMethod獲取注解屬性的方法和獲取MyType是一樣的。

總體看下來是不是很簡單,源碼已經(jīng)傳到git上,沒看懂的同學(xué)跑下例子就秒懂了,也可以在評論區(qū)留言提問。

源碼地址:https://github.com/feiweiwei/java-monkey01-sample

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,665評論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內(nèi)容