編譯時(shí)注解處理器AnnotationProcessor的使用

前言

在前面寫的EventBus的源碼解析的文章中提到了編譯時(shí)注解的概念藐吮,在編譯期EventBusAnnotationProcessor會(huì)生成一個(gè)MyEventIndex的java類文件,并把帶有@subscribe的方法信息保存在該方法中,并在注冊(cè)的時(shí)候獲取訂閱方法信息橘忱,那么編譯器注解是如何工作的恃鞋?下面就給大家講講究竟這是個(gè)什么東西吧宪赶。

什么是注解告抄?

從JDK5開始,Java增加了注解愤估,注解是代理里特殊的標(biāo)記帮辟,這些標(biāo)記可以在編譯、類加載玩焰、運(yùn)行時(shí)被讀取织阅,并執(zhí)行相應(yīng)的處理。通過使用注解震捣,開發(fā)人員可以在不改變?cè)羞壿嫷那闆r下荔棉,在源文件中嵌入一些補(bǔ)充信息。代碼分析工具蒿赢,開發(fā)工具和部署工具可以通過這些補(bǔ)充信息進(jìn)行驗(yàn)證润樱,處理或者進(jìn)行部署。

注解的分類

(1)標(biāo)注注解

  • @Overrride:對(duì)覆蓋超類中的方法進(jìn)行標(biāo)記羡棵,如果被標(biāo)記的方 法并沒有實(shí)際覆蓋超類中的方法壹若,則編譯器會(huì)發(fā)出警告。
  • @Deprecate:提示開發(fā)者該方法已經(jīng)廢棄皂冰,不推薦使用店展。
  • @SupperWarnings:選擇性地取消特定代碼中的警告。

(2)元注解
用來標(biāo)注其它注解而創(chuàng)建的新注解秃流,元注解的類型有以下幾種:

  • @Target:注解所修飾的對(duì)象范圍
  • @Inherited:表示注解可以被繼承
  • @Documented:表示這個(gè)注解應(yīng)該被JavaDoc工具記錄
  • @Rentation:用來聲明注解的保留策略
  • @Repeable:JDK8新增赂蕴,允許一個(gè)注解在同一聲明類型(類,屬性或方法)上多次使用舶胀。

其中@Target注解的取值是一個(gè)ElementType類型的枚舉概说,其中有一下幾種取值,對(duì)應(yīng)不同的對(duì)象范圍嚣伐。

  • ElementType.TYPE:能修飾類糖赔,接口或者枚舉類型。
  • ElementType.FIELD:能修飾成員變量
  • ElementType. METHOD:能修飾方法
  • ElementType. PARAMETER:能修飾參數(shù)
  • ElementType. CONSTRUCTOR:能修飾構(gòu)造方法
  • ElementType.LOCAL_VARIABLE:能修飾局部變量
  • ElementType. ANNOTATION_TYPE:能修飾注解
  • ElementType. PACKAGE:能修飾包
  • ElementType.TYPE_PARAMETER:能修飾參數(shù)聲明
  • ElementType.TYPE_USE:使用類型

其中@Retention注解有3種類型轩端,分別表示不同級(jí)別的保留策略放典。

  • RetentionPolicy.SOURCE:源碼級(jí)注解,注解信息只會(huì)保留在Java源碼中,源碼在編譯后奋构,注解信息將會(huì)被丟棄骨田,不會(huì)保留在.class文件中。

  • RetentionPolicy.CLASS:編譯時(shí)注解声怔。注解信息會(huì)保留在.java源碼以及.class中。當(dāng)運(yùn)行java程序時(shí)舱呻,JVM會(huì)丟棄該注解信息醋火,不會(huì)保留在JVM中,許多注解類框架箱吕,如EventBus芥驳,ButterKnife他們都是使用編譯時(shí)注解信息開發(fā)的,針對(duì)編譯時(shí)注解我們采用AbstractProcessor來處理注解信息茬高。

  • RetentionPolicy.RUNTIME:運(yùn)行注解兆旬。當(dāng)運(yùn)行Java程序時(shí),JVM也會(huì)保留該注解信息怎栽,可以通過反射獲取該注解信息丽猬。

AnnotationProcessor的使用

(1)新建annotations庫

  • 新建一個(gè)Java Lib庫annotations,其gradle配置文件如下:
apply plugin: 'java'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

  • 定義一個(gè)注解BindView注解熏瞄,其源碼如下:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
    int value() default 0;
}

(2)新建processor 庫

  • 新建一個(gè)processor java lib庫脚祟,并引用上面定義的annotations庫
apply plugin: 'java'

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    compile project(path: ':annotations')
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"
  • 定義一個(gè)ButterKnifeProcessor類繼承AbstractProcessor類
package com.mtime.processor;

import com.mtime.annotations.BindView;

import java.io.BufferedWriter;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;

import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.mtime.annotations.BindView")

public class ButterKnifeProcessor extends AbstractProcessor {
    private Messager mMessage;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mMessage = processingEnvironment.getMessager();

    }


    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        for(Element element : roundEnvironment.getElementsAnnotatedWith(BindView.class)){
            if(element.getKind() == ElementKind.FIELD){
                mMessage.printMessage(Diagnostic.Kind.NOTE,"printMessage : " + element.toString());

            }
        }
        return true;
    }


    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }


    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

}

ButterKnifeProcessor 注解處理器中所有方法介紹如下:

  • init():會(huì)自動(dòng)被注解處理工具調(diào)用,并傳入ProcessingEnviroment參數(shù)强饮,通過該參數(shù)可以獲取到很多有用的工具類:** Elements , Types , Filer **等等

  • process(Set<? extends TypeElement> annoations, RoundEnvironment roundEnv):Annotation Processor掃描出的結(jié)果會(huì)存儲(chǔ)進(jìn)roundEnv中由桌,可以在這里獲取到注解內(nèi)容,編寫你的操作邏輯邮丰。注意,process()函數(shù)中不能直接進(jìn)行異常拋出,否則的話行您,運(yùn)行Annotation Processor的進(jìn)程會(huì)異常崩潰,然后彈出一大堆讓人捉摸不清的堆棧調(diào)用日志顯示.

  • getSupportedAnnotationTypes(): 該函數(shù)用于指定該自定義注解處理器(Annotation Processor)是注冊(cè)給哪些注解的(Annotation),注解(Annotation)指定必須是完整的路徑。

  • getSupportedSourceVersion():用于指定你的java版本剪廉,一般返回:SourceVersion.latestSupported()

  • 注解處理器的注冊(cè)過程

為了能使用注解處理器娃循,需要用一個(gè)服務(wù)器文件來注冊(cè)它,現(xiàn)在我們就來創(chuàng)建這個(gè)服務(wù)器文件斗蒋,首先在processor庫的main目錄下新建resources資源文件夾淮野,接下來在resources文件夾下新建META-INF/services目錄文件夾。最后在META-INF/ services中創(chuàng)建javax.annotation.processing.Processor文件吹泡,文件的內(nèi)容是我們上面定義的com.mtime.processor.ButterKnifeProcessor類的完整路徑骤星。

processor的注冊(cè).png

(3) 主項(xiàng)目中調(diào)用

  • 我們?cè)谥黜?xiàng)目中引用annotations和processor兩個(gè)庫,并在gradle配置文件中添加‘a(chǎn)nnotationProcessorOptions.includeCompileClasspath = true’的配置爆哑,如果不添加該配置洞难,編譯時(shí)將會(huì)報(bào)錯(cuò)。
 android {
    compileSdkVersion 26
  defaultConfig {
        applicationId "com.mtime.myprocessor"
  minSdkVersion 15
  targetSdkVersion 26
  versionCode 1
  versionName "1.0"
  testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  javaCompileOptions{
            annotationProcessorOptions.includeCompileClasspath = true
  }
    }
  • 在Activity中模擬使用BindView注解揭朝,如下所示:
public class MainActivity extends Activity {
    @BindView(R.id.tv_text)
    TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

最后我們可以看看gradle打印出的日志信息下:

日志信息.png

也就是說我們獲取到了包含@BindView注解的field textView队贱,那么我們就可以通過反射的方式對(duì)其進(jìn)行初始化色冀,這也是butterKnife框架的開發(fā)原理。EventBus其原理也是一樣的柱嫌,在編譯的時(shí)候就把注解信息給保存下來锋恬,這就省了我們?cè)诖a中尋找注解的開銷,這也是注解類框架非常流行的原因编丘,使用很小的開銷与学,就可以方便我們不用地去寫一些重復(fù)性的代碼。
github地址:https://github.com/Fredlxy/AnnotationProcessorDemo.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嘉抓,一起剝皮案震驚了整個(gè)濱河市索守,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抑片,老刑警劉巖卵佛,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異敞斋,居然都是意外死亡截汪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門植捎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挫鸽,“玉大人,你說我怎么就攤上這事鸥跟《迹” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵医咨,是天一觀的道長(zhǎng)枫匾。 經(jīng)常有香客問我,道長(zhǎng)拟淮,這世上最難降的妖魔是什么干茉? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮很泊,結(jié)果婚禮上角虫,老公的妹妹穿的比我還像新娘。我一直安慰自己委造,他們只是感情好戳鹅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著昏兆,像睡著了一般枫虏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天隶债,我揣著相機(jī)與錄音腾它,去河邊找鬼。 笑死死讹,一個(gè)胖子當(dāng)著我的面吹牛瞒滴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赞警,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妓忍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了仅颇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤碘举,失蹤者是張志新(化名)和其女友劉穎忘瓦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體引颈,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡耕皮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝙场。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凌停。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖售滤,靈堂內(nèi)的尸體忽然破棺而出罚拟,到底是詐尸還是另有隱情,我是刑警寧澤完箩,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布赐俗,位于F島的核電站,受9級(jí)特大地震影響弊知,放射性物質(zhì)發(fā)生泄漏阻逮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一秩彤、第九天 我趴在偏房一處隱蔽的房頂上張望叔扼。 院中可真熱鬧,春花似錦漫雷、人聲如沸瓜富。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽食呻。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仅胞,已是汗流浹背每辟。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留干旧,地道東北人渠欺。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像椎眯,于是被迫代替她去往敵國(guó)和親挠将。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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

  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來 What:A...
    zlcook閱讀 29,158評(píng)論 15 116
  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,166評(píng)論 0 2
  • 整體Retrofit內(nèi)容如下: 1编整、Retrofit解析1之前哨站——理解RESTful 2舔稀、Retrofit解析...
    隔壁老李頭閱讀 6,483評(píng)論 4 31
  • 上一篇我們講解了ButterKnife的設(shè)計(jì)思想,理解了ButterKnife綁定相關(guān)源碼的實(shí)現(xiàn)邏輯掌测。但是它是怎么...
    Ihesong閱讀 1,001評(píng)論 0 2
  • 一内贮、什么是注解? 注解對(duì)于開發(fā)人員來講既熟悉又陌生汞斧,熟悉是因?yàn)橹灰闶亲鲩_發(fā)夜郁,都會(huì)用到注解(常見的@Overrid...
    _Justin閱讀 1,355評(píng)論 0 10