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

一、元注解

@interface是一種自定義的注解類(lèi)型,他可以由四種元注解修飾,分別是@Target、@Retention、@Documented、@Inherited。

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

在其他類(lèi)中使用自定義的注解:

/*@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 {
    //主要用于修飾類(lèi),接口,枚舉類(lèi)型等
    TYPE,

    //修飾作用域
    FIELD,

    //修飾方法
    METHOD,

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

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

    //修飾局部變量
    LOCAL_VARIABLE,

    //用于描述包
    PACKAGE,
}

2、@Retention

@Retention:主要用于控制注解的生命周期,主要有三種類(lèi)型: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:如果父類(lèi)被注解修飾的話,子類(lèi)會(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)前類(lèi)是否使用這個(gè)注解
        if (child.getClass().isAnnotationPresent(MyFirstAnnotation.class)){
            System.out.println("isAnnotationPresent:true");
        }
    }
}

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

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

首先定義三個(gè)注釋?zhuān)鼊e用于修飾類(lèi),方法,成員變量

@Target(ElementType.TYPE)//用于修飾類(lèi)
@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)建的保存用戶信息的類(lèi)當(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ì)象上直接存在的所有注釋?zhuān)ú话ǜ割?lèi)中Inherited修飾的注解)。

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

注解處理器(Annotation Processor)是javac的一個(gè)工具,它用來(lái)在編譯時(shí)掃描和處理注解(由于是在編譯期間就開(kāi)始處理注釋?zhuān)虼俗⑨尩纳芷贎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)建注釋處理器的類(lèi),需要繼承AbstractProcessor,

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

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

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

    //用于指定對(duì)應(yīng)的注釋?zhuān)祷匾粋€(gè)String集合(可被上面的注釋代替)
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }

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

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

@Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        //用于創(chuàng)建java文件,并寫(xiě)入它
        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注釋的元素(這邊的元素可以是類(lèi),方法,變量等等)。
        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類(lèi)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容