Android自定義注解學(xué)習(xí)筆記

一换淆、元注解

@interface是一種自定義的注解類型,他可以由四種元注解修飾真屯,分別是@Target脸候、@Retention、@Documented绑蔫、@Inherited运沦。

//如何使用元注解修飾創(chuàng)建的自定義注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyFirstAnnotation {
    String name() default "author";
    int age();
}

在其他類中使用自定義的注解:

/*@interface MyFirstAnnotation后的MyFirstAnnotation就是自定義注解的名字。
*在創(chuàng)建自定義注解時(shí)如果沒(méi)有設(shè)置默認(rèn)值的話就必須進(jìn)行賦值操作(由于age沒(méi)有默認(rèn)值配深,所以這邊必須賦值)
*/
@MyFirstAnnotation(age = 23)
public class Demo {
}

1携添、@Target

@target:主要用來(lái)設(shè)置注解的使用范圍

public enum ElementType {
    //主要用于修飾類,接口篓叶,枚舉類型等
    TYPE,

    //修飾作用域
    FIELD,

    //修飾方法
    METHOD,

    //修飾參數(shù)
    PARAMETER,

    //修飾構(gòu)造函數(shù)
    CONSTRUCTOR,

    //修飾局部變量
    LOCAL_VARIABLE,

    //用于描述包
    PACKAGE,
}

2烈掠、@Retention

@Retention:主要用于控制注解的生命周期,主要有三種類型:SOURCE缸托、CLASS左敌、RUNTIME。

public enum RetentionPolicy {
    //源碼級(jí)別俐镐,只存在于源碼中矫限,用于與編譯器交互進(jìn)行代碼檢測(cè)(@Override,@SuppressWarings等)
    //一般用于生成源碼級(jí)別的框架
    SOURCE,
    //字節(jié)碼級(jí)別,注解的信息會(huì)被保留在class文件中,但是不會(huì)存在與JVM中
    CLASS,
    //運(yùn)行時(shí)級(jí)別叼风,存在于JVM虛擬機(jī)中取董,主要用于反射來(lái)獲取相關(guān)的信息。一般用于生成運(yùn)行級(jí)別的框架
    RUNTIME;
}

3咬扇、@Documented與@Inherited

@Documented:被修飾的注解會(huì)生成到j(luò)avadoc中
@Inherited:如果父類被注解修飾的話甲葬,子類會(huì)繼承這個(gè)注釋

public class Demo {

    @MyFirstAnnotation(age = 23)
    private static class Father {

    }
    private static class Child extends Father{

    }

    public static void main(String... args){
        Father child=new Child();
        //isAnnotationPresent可以用來(lái)判斷當(dāng)前類是否使用這個(gè)注解
        if (child.getClass().isAnnotationPresent(MyFirstAnnotation.class)){
            System.out.println("isAnnotationPresent:true");
        }
    }
}

二、反射機(jī)制運(yùn)行處理的注解

自定義的注解主要有兩種形式懈贺,一種是通過(guò)反射來(lái)獲取對(duì)象相應(yīng)的注釋经窖,另一種是通過(guò)注釋處理器在編譯時(shí)處理注解。
運(yùn)用反射機(jī)制去獲取注釋對(duì)象梭灿,要求注釋必須設(shè)置為@Retention(RetentionPolicy.RUNTIME)画侣,即在JVM運(yùn)行時(shí)也要保存對(duì)應(yīng)的注釋。

首先定義三個(gè)注釋堡妒,更別用于修飾類配乱,方法,成員變量

@Target(ElementType.TYPE)//用于修飾類
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFirstAnnotation {
    String name() default "author";
}
@Target(ElementType.FIELD)//用于修飾成員變量
@Retention(RetentionPolicy.RUNTIME)
public @interface MySecondAnnotation {
    String gender() default "male";
}
@Target(ElementType.METHOD)//用于修飾方法
@Retention(RetentionPolicy.RUNTIME)
public @interface MyThirdAnnotation {
    String likeFood() ;
    String work() ;
}

然后在創(chuàng)建的保存用戶信息的類當(dāng)中使用新建的注釋

@MyFirstAnnotation(name = "lilei")
public class User {

    @MySecondAnnotation(gender = "male")
    public String userInfo = "";

    @MyThirdAnnotation(likeFood = "milk", work = "teacher")
    public void getOtherInformation() {
    }
}

因?yàn)镃lass皮迟,Method搬泥,F(xiàn)ield都實(shí)現(xiàn)了AnnotatedElement接口,所以可以調(diào)用isAnnotationPresent()方法去判斷當(dāng)前對(duì)象是否被對(duì)應(yīng)的注釋給修飾伏尼,也可以通過(guò)getAnnotation()獲取對(duì)應(yīng)注釋的實(shí)例忿檩。

void getUserInfo(String className) {
        Class c =Class.forName(className);
        //通過(guò)isAnnotationPresent()方法判斷是否被MyFirstAnnotation給修飾
        if (c.isAnnotationPresent(MyFirstAnnotation.class)) {
            //通過(guò)getAnnotation()獲取對(duì)應(yīng)注釋的實(shí)例,接著通過(guò)調(diào)用對(duì)應(yīng)的方法就可以獲取name值爆阶。
            MyFirstAnnotation annotation = (MyFirstAnnotation) c.getAnnotation(MyFirstAnnotation.class);
            name.setText(annotation.name());
        }
        //field和method的獲取對(duì)應(yīng)注釋實(shí)例的過(guò)程與Class是一樣的
        for (Field field : c.getDeclaredFields()) {
            if (field.isAnnotationPresent(MySecondAnnotation.class)) {
                MySecondAnnotation annotation = field.getAnnotation(MySecondAnnotation.class);
                sex.setText(annotation.gender());
            }
        }
        for (Method method : c.getMethods()) {
            MyThirdAnnotation annotation = method.getAnnotation(MyThirdAnnotation.class);
            if (annotation != null) {
                like.setText(annotation.likeFood());
                work.setText(annotation.work());
            }
        }
    }

除了上面介紹的isAnnotationPresent()和getAnnotation(),AnnotatedElement接口中還有g(shù)etAnnotations()和getDeclaredAnnotations()兩個(gè)方法燥透,分別用于獲取該對(duì)象的所有注釋和該對(duì)象上直接存在的所有注釋(不包括父類中Inherited修飾的注解)。

三辨图、注釋處理器來(lái)處理注釋

注解處理器(Annotation Processor)是javac的一個(gè)工具班套,它用來(lái)在編譯時(shí)掃描和處理注解(由于是在編譯期間就開(kāi)始處理注釋,因此注釋的生命周期@Retention(***)可以設(shè)置為三種中的任意一種)故河。注解處理器的主要作用就是解析注解吱韭,獲取注解相對(duì)應(yīng)的值,然后以此為基礎(chǔ)進(jìn)行邏輯操作忧勿。

使用注釋處理器首先需要?jiǎng)?chuàng)建一個(gè)首先創(chuàng)建一個(gè)Java Library,并且引用auto-Service的庫(kù)杉女。
compile 'com.google.auto.service:auto-service:1.0-rc3'

然后創(chuàng)建注釋處理器的類,需要繼承AbstractProcessor鸳吸,

//autoService主要用于向javac注冊(cè)我們自定義的注釋解釋器的(當(dāng)然我們也可以自己定義注釋解釋器)
@AutoService(Processor.class)
//用于確定我們使用的java版本,在這里我們?cè)O(shè)置為java7(使用這個(gè)和下面兩個(gè)注解可以代替
//getSupportedAnnotationTypes()和getSupportedSourceVersion()方法)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//用于指定相應(yīng)的注釋(需要完整的包名)
@SupportedAnnotationTypes("com.example.MyFourthAnnotation")
public class MyProcessor extends AbstractProcessor {

    //編譯期間速勇,init()會(huì)自動(dòng)被注解處理工具調(diào)用晌砾,并傳入ProcessingEnviroment參數(shù),
    //通過(guò)該參數(shù)可以獲取到很多有用的工具類: Elements , Types , Filer 等等
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv)烦磁;
    }

    //Annotation Processor掃描出的結(jié)果會(huì)存儲(chǔ)進(jìn)roundEnv中养匈,可以在這里獲取到注解內(nèi)容哼勇,編寫你的操作邏輯(比如生成java文件)
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }

    //用于指定對(duì)應(yīng)的注釋,返回一個(gè)String集合(可被上面的注釋代替)
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }

    //用來(lái)指定你使用的Java版本(可被上面的注釋代替)
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return super.getSupportedSourceVersion();
    }
}

在init()方法中可以獲取到許多工具類,通過(guò)這些工具類呕乎,我們可以打印日志积担,進(jìn)行java文件的創(chuàng)建與寫入(不能在原有的java文件上進(jìn)行修改),或者使用Elements進(jìn)行獲取包名等操作猬仁。

@Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        //用于創(chuàng)建java文件帝璧,并寫入它
        Filer mFiler = processingEnv.getFiler();
        //一些實(shí)用的方法
        Elements mElements = processingEnv.getElementUtils();
        //用于打印具體的消息(通過(guò)messager來(lái)打印消息)
        Messager mMessager = processingEnv.getMessager();
    }

在process()中的roundEnv可以獲取到所有使用MyFourthAnnotation注釋的元素(實(shí)現(xiàn)了AnnotatedElement接口),因此可以輕松拿到注釋的實(shí)例湿刽。然后就可以根據(jù)需求做對(duì)應(yīng)的操作的烁。這邊只是做了簡(jiǎn)單的打印工作。

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //獲取所有使用MyFourthAnnotation注釋的元素(這邊的元素可以是類诈闺,方法渴庆,變量等等)。
        for (Element element : roundEnv.getElementsAnnotatedWith(MyFourthAnnotation.class)) {
            MyFourthAnnotation annotation = element.getAnnotation(MyFourthAnnotation.class);
            int id = annotation.value();
            //設(shè)置為Diagnostic.Kind.ERROR時(shí)雅镊,會(huì)編譯不過(guò)去襟雷,報(bào)錯(cuò)
            mMessager.printMessage(Diagnostic.Kind.NOTE, "MyFourthAnnotation---->value:" + id);
        }
        return false;
    }

在app編譯的過(guò)程中會(huì)打印下面的log.


打印的LOG

注釋處理器的功能非常強(qiáng)大,可以通過(guò)跟javapoet庫(kù)配合使用生成實(shí)用的Java類仁烹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耸弄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晃危,更是在濱河造成了極大的恐慌叙赚,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僚饭,死亡現(xiàn)場(chǎng)離奇詭異震叮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)鳍鸵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門苇瓣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人偿乖,你說(shuō)我怎么就攤上這事击罪。” “怎么了贪薪?”我有些...
    開(kāi)封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵媳禁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我画切,道長(zhǎng)竣稽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮毫别,結(jié)果婚禮上娃弓,老公的妹妹穿的比我還像新娘。我一直安慰自己岛宦,他們只是感情好台丛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著砾肺,像睡著了一般挽霉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上债沮,一...
    開(kāi)封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天炼吴,我揣著相機(jī)與錄音,去河邊找鬼疫衩。 笑死硅蹦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的闷煤。 我是一名探鬼主播童芹,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鲤拿!你這毒婦竟也來(lái)了假褪?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤近顷,失蹤者是張志新(化名)和其女友劉穎生音,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體窒升,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缀遍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饱须。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片域醇。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蓉媳,靈堂內(nèi)的尸體忽然破棺而出譬挚,到底是詐尸還是另有隱情,我是刑警寧澤酪呻,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布减宣,位于F島的核電站,受9級(jí)特大地震影響玩荠,放射性物質(zhì)發(fā)生泄漏蚪腋。R本人自食惡果不足惜丰歌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一姨蟋、第九天 我趴在偏房一處隱蔽的房頂上張望屉凯。 院中可真熱鬧,春花似錦眼溶、人聲如沸悠砚。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)灌旧。三九已至,卻和暖如春绰筛,著一層夾襖步出監(jiān)牢的瞬間枢泰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工铝噩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衡蚂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓骏庸,卻偏偏與公主長(zhǎng)得像毛甲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子具被,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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