Java annotation processor學(xué)習(xí)

?????? 之前沒(méi)有接觸過(guò) Annotation Processor(注解處理器)這個(gè)概念,當(dāng)研究Butterknife框架實(shí)現(xiàn)機(jī)制時(shí),發(fā)現(xiàn)Annotation Processor是Butterknife框架實(shí)現(xiàn)機(jī)制的核心要點(diǎn)。原來(lái)代碼本身能生成代碼丑搔,我之前還停留在程序運(yùn)行階段利用反射去讀取注解再進(jìn)行處理的階段勋磕,有了Annotation Processor之后,我們就能在編譯時(shí)根據(jù)注解動(dòng)態(tài)生成代碼埠忘,執(zhí)行與注解關(guān)聯(lián)的邏輯,可以有效避免反射的性能問(wèn)題馒索,非常高大上莹妒。

Annotation(注解)分類:

????? 按照運(yùn)行機(jī)制,注解分三類:

????????? 源碼注解(RetentionPolicy.SOURCE): 注解只在源碼中存在绰上,編譯成.class文件就不存在了旨怠。

??????????編譯時(shí)注解(RetentionPolicy.CLASS):注解在.class文件里面存在。?

????????? 運(yùn)行時(shí)注解(RetentionPolicy.RUNTIME):在運(yùn)行階段還起作用蜈块,甚至?xí)绊戇\(yùn)行邏輯的注解?鉴腻。

???????? 上面三類中,Annotation Processor處理的對(duì)象主要針對(duì) 編譯時(shí)注解

Annotation Processor是什么百揭?

????????它是一個(gè)工具爽哎,包含在javac中,Java5之后才有(因?yàn)锳nnotation也是Java5才有的)器一,但Java6中才有API供使用课锌。

Annotation Processor的作用?

??????? 編譯時(shí)掃描和處理注解(主要是編譯時(shí)注解)祈秕。一個(gè)注解的注解處理器渺贤,以Java代碼(或者編譯過(guò)的字節(jié)碼)作為輸入,生成文件(一般是.java文件)作為輸出请毛。

??????? 通過(guò)Annotation Processor志鞍,我們獲取到注解,然后再生成相關(guān)Java代碼方仿,注意:注解處理器不能更新手寫的Java代碼固棚,比如在一個(gè)類中增加方法街州。新生成Java代碼會(huì)與手寫的代碼一樣,會(huì)被javac編譯玻孟。

如何使用Annotation Processor?

第一步:實(shí)現(xiàn)處理函數(shù)

?????? JDK提供了javax.annotation.processing.AbstractProcessor來(lái)處理注解唆缴,這是一個(gè)抽象類,需要實(shí)現(xiàn)其中幾個(gè)方法黍翎。

?????? public synchronized void init(ProcessingEnvironment var1);

????????????????? 它會(huì)被注解處理工具調(diào)用面徽,并輸入ProcessingEnvironment 參數(shù)。ProcessingEnvironment 提供很多有用的工具類匣掸,比如Elements趟紊、Types、Filer等

?????? public Set<String> getSupportedAnnotationTypes();??

?????????????????? 必須指定碰酝,返回值是一個(gè)字符串的集合霎匈,包含本處理器想要處理的注解類型的合法全稱。即你在這里定義你的注解處理器處理哪些注解

?????? public SourceVersion getSupportedSourceVersion();

?????????????????? 用來(lái)指定使用的Java版本送爸,一般返回 SourceVersion.latestSupported();

??????? public abstract boolean process(Set<? extends TypeElement> var1, RoundEnvironment var2);?

?????????????????? 這里實(shí)現(xiàn)主要的處理邏輯铛嘱,利用init函數(shù)里得到的工具類Elements、Types袭厂、 Filer 等來(lái)處理注解墨吓,并自動(dòng)生成代碼。

?????????????????? 在這里除了可以調(diào)用JDK里的api外纹磺,還可以使用很多第三方庫(kù)帖烘,因?yàn)閖avac會(huì)啟動(dòng)一個(gè)完整Java虛擬機(jī)來(lái)運(yùn)行注解處理器。

第二步:注冊(cè)Annotation Processor

??????? 實(shí)現(xiàn)好AbstractProcessor之后橄杨,比如名叫MyProcessor秘症,需要將MyProcessor注冊(cè)到j(luò)avac中。首先需要將MyProcessor打包成jar式矫,比如叫MyProcessor.jar乡摹,在這個(gè)jar中,你需要打包一個(gè)特定的文件javax.annotation.processing.ProcessorMETA-INF/services路徑下

???????? javax.annotation.processing.Processor這個(gè)文件里的內(nèi)容是我們實(shí)現(xiàn)的Annotation Processor的列表,比如:

???? ?com.example.MyProcessor1

?? ? ?com.example.MyProcessor2

??? 這說(shuō)明我們可以實(shí)現(xiàn)不止一個(gè)Annotation Processor衷佃。

最后把MyProcessor.jar放到你的builpath中趟卸,javac會(huì)自動(dòng)檢查和讀取javax.annotation.processing.Processor中的內(nèi)容蹄葱,并且注冊(cè)MyProcessor作為注解處理器氏义。

如何生成META-INF等目錄以及javax.annotation.processing.Processor文件?

??第一種方式:手工編寫生成图云。

???? 創(chuàng)建resources 源文件夾 惯悠、然后在里面創(chuàng)建META-INF/services/javax.annotation.processing.Processor文件,這個(gè)文件寫處理器的類完整路徑竣况。

??第二種方式:利用google提供的AutoService注解處理器克婶,等于說(shuō)我們的注解處理器需要被AutoService注解處理器先進(jìn)行處理。使用方式(針對(duì)Android Studio里的grandle配置):

? 1.注解處理器模塊的build.gradle文件中引入

???????? compile 'com.google.auto.service:auto-service:1.0-rc2'

? 2.在public class MyProcessor上面使作注解:@AutoService(Processor.class)

如何將MyProcessor.jar放到builpath中?

?? 需要將Annotation Processor所在的工程單獨(dú)成一個(gè)java標(biāo)準(zhǔn)工程

??1.使用android-apt Gradle插件:

apply plugin: 'com.neenbedankt.android-apt'

?? 其有兩個(gè)作用:?

???a.允許配置只在編譯時(shí)作為注解處理器的依賴情萤,而不添加到最后的APK或library(因?yàn)檫\(yùn)行時(shí)用不到鸭蛙,沒(méi)必要增加apk的體積)

???b.設(shè)置源路徑,使注解處理器生成的代碼能被Android Studio正確的引用

2.在dependencies里 用apt替代compile來(lái)引用處理器模塊

apt project(':annotation-comprocesser')

工具類Elements筋岛、Types娶视、Filer等的使用

??? 作用:Elements是處理Element的工具類,Element代表程序的元素,例如包睁宰、類或者方法肪获,可以理解成源代碼;TypeElement代表的是源代碼中的類型元素,例如類柒傻、域孝赫、方法等;從TypeElement中能獲取類的名字,但是你獲取不到類的信息红符,例如它的父類青柄,這個(gè)需要從TypeMirror獲取,而TypeMirror需要調(diào)用Element的asType()函數(shù)

? 使用步驟:

? 在process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)函數(shù)中

? 1.利用roundEnv.getElementsAnnotatedWith(xxx.class)可以得到xxx注解標(biāo)注的Element集合

???? Element表示一個(gè)程序元素预侯,比如類本身刹前、類的變量、方法等雌桑,這個(gè)Element集合中將所有類中的元素都列出來(lái)了喇喉,為了方便生成java代碼,需要要將Element集合按照類進(jìn)行分類校坑,以便生成操作該類的類

? 2.通過(guò)Element的getEnclosingElement()函數(shù)拣技,可以獲取該元素最外層的元素,比如方法耍目,其肯定屬于某個(gè)類膏斤,故其最外層的元素就是類,此元素用TypeElement表示邪驮,再通過(guò)TypeElement的getQualifiedName()就能拿到類全名

??? 如果我們只想獲取特定類型上的注解元素莫辨,比如只想要標(biāo)準(zhǔn)在class上的元素,可以用Element的getKind()函數(shù)來(lái)獲取元素的類型(比如返回ElementKind.CLASS表示該元素是類)來(lái)過(guò)濾毅访,比如這樣:

??? // 檢查被注解為@Factory的元素是否是一個(gè)類 ?????

??? if (annotatedElement.getKind() != ElementKind.CLASS) {
???????...
???? }

??? Elements的getPackageOf(Element type)能得到包信息

? 3.如果我們知道一個(gè)Element是ElementKind.CLASS或ElementKind.Field沮榜,可以強(qiáng)轉(zhuǎn)成TypeElement,然后用TypeElement的getAnnotation(xxx.class)取出注解的實(shí)例信息喻粹,這些信息是使用注解時(shí)蟆融,相當(dāng)于傳入的實(shí)參

??4.注解所在類拿到了,注解的信息也拿到了守呜,被注解的元素也拿到了型酥,最后我們可以通過(guò)這些信息產(chǎn)生新的文件了山憨。

? 5.生成代碼∶趾恚可以使用第三方庫(kù)Javapoet來(lái)加快生成速度郁竟,或者直接使用FileWriter

???? 如果要生成java的源碼,需要用到Filer

需要注意的問(wèn)題

1.注解處理過(guò)程是一個(gè)有序的循環(huán)過(guò)程由境,因?yàn)樯傻男碌奈募幸灿锌赡馨枰惶幚淼淖⒔猓?雖然需要經(jīng)過(guò)多輪處理枪孩,但不能重載或者重新創(chuàng)建已經(jīng)生成的源代碼,所以最后生成文件后藻肄,需要將為了生成文件而保存的緩存清除蔑舞,避免第二輪又調(diào)用process去試圖重復(fù)生成文件而導(dǎo)致異常

2.為什么要單獨(dú)將Annotation Processor打包成一個(gè)jar,因?yàn)锳nnotation Processor模塊只是為了編譯嘹屯,在運(yùn)行時(shí)不需要攻询,為了使最后的程序不過(guò)于臃腫,而且apk中.dex文件也有65k個(gè)方法的限制州弟,同時(shí)Annotation Processor是可以包含其它庫(kù)的钧栖,所以要避免將其打包進(jìn)去。

3.Annotation Processor是發(fā)生在類型擦除(type erasure)之前的婆翔,所以它可以做很多復(fù)雜的事情


???????

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拯杠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子啃奴,更是在濱河造成了極大的恐慌潭陪,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件最蕾,死亡現(xiàn)場(chǎng)離奇詭異依溯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)瘟则,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門黎炉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人醋拧,你說(shuō)我怎么就攤上這事慷嗜。” “怎么了丹壕?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵庆械,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我雀费,道長(zhǎng)干奢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任盏袄,我火速辦了婚禮忿峻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辕羽。我一直安慰自己逛尚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布刁愿。 她就那樣靜靜地躺著绰寞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铣口。 梳的紋絲不亂的頭發(fā)上滤钱,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音脑题,去河邊找鬼件缸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛叔遂,可吹牛的內(nèi)容都是我干的他炊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼已艰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼痊末!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起哩掺,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凿叠,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后嚼吞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幔嫂,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年誊薄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了履恩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呢蔫,死狀恐怖切心,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情片吊,我是刑警寧澤绽昏,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站俏脊,受9級(jí)特大地震影響全谤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜爷贫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一认然、第九天 我趴在偏房一處隱蔽的房頂上張望补憾。 院中可真熱鬧,春花似錦卷员、人聲如沸盈匾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)削饵。三九已至,卻和暖如春未巫,著一層夾襖步出監(jiān)牢的瞬間窿撬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工叙凡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留劈伴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓狭姨,卻偏偏與公主長(zhǎng)得像宰啦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饼拍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

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

  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來(lái) What:A...
    zlcook閱讀 29,129評(píng)論 15 116
  • 整體Retrofit內(nèi)容如下: 1赡模、Retrofit解析1之前哨站——理解RESTful 2、Retrofit解析...
    隔壁老李頭閱讀 6,426評(píng)論 4 31
  • Java 中的注解(Annotation) 是一個(gè)很方便的特性在Spring當(dāng)中得到了大量的應(yīng)用 , 我們也可以開...
    _秋天閱讀 8,818評(píng)論 3 22
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理师抄,服務(wù)發(fā)現(xiàn)漓柑,斷路器,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 前面寫了Android 開發(fā):由模塊化到組件化(一),很多小伙伴來(lái)問(wèn)怎么沒(méi)有Demo啊?之所以沒(méi)有立刻放demo的...
    涅槃1992閱讀 8,016評(píng)論 4 37