[譯]使用注解處理器生成代碼-2 注解處理器

原文請看摸我
?這是有關(guān)注解代碼生成技術(shù)系列博文的第二部分。在第一部分(摸我)中,我們介紹了注解的基本概念與用法。
?在本篇博文中我們將介紹注解處理器的基本概念和使用運(yùn)行方法。

介紹

注解功能強(qiáng)大。你可以使用注解來設(shè)置各類元數(shù)據(jù)或者配置信息,語法格式優(yōu)雅并且功能強(qiáng)大。
?從目前我們了解的知識來看,注解比起Javadoc來有很多優(yōu)勢,但是這些好像都不足以委員會(huì)將其加入java語言之中。那么,我們可以更好的利用和了解注解嗎?當(dāng)然可以啦:

  • 在運(yùn)行時(shí)刻,帶有runtime retention policy的注解可以通過反射獲得,Class類中的getAnnotation()getAnnotations()方法可以做到這些。
  • 在編譯時(shí)刻,注解處理器可以處理在編譯時(shí)發(fā)現(xiàn)的各類注解。

注解處理器API

當(dāng)注解在Java 5 首次被引入時(shí),注解處理器API還不是很成熟和規(guī)范化。處理注解需要一個(gè)單獨(dú)的工具,叫做apt,注解處理工具;還需要Mirror API(com.sun.mirror),用于編寫自定義處理器。
?從java 6開始,注解處理通過JSR 269(2)被標(biāo)準(zhǔn)化,被集成進(jìn)標(biāo)準(zhǔn)庫并且apt無縫的集成到j(luò)ava編譯器javac中。
?因?yàn)槲覀冎辉敿?xì)講述Java 6 中的注解處理器相關(guān)API,你可以在這里這里找到關(guān)于java 5中注解的更多信息,并在這里找到一些例子。
?自定義注解處理器只是實(shí)現(xiàn)了javax.annotation.processing.Processor接口并準(zhǔn)從指定的協(xié)議。為了我們的便利,自定義處理器一些常用的功能都由javax.annotation.processing.AbstractProcessor這個(gè)類給出了抽象實(shí)現(xiàn)。
?自定義注解處理器可以使用到一下三個(gè)注解來配置自己:

  • javax.annotation.processing.SupportedAnnotationTypes:這個(gè)注解用來注冊注解處理器要處理的注解類型。有效值為完全限定名(就是帶所在包名和路徑的類全名)-通配符(此次英語原文為Wildcards,就是?這個(gè)符號代表的類型。比如說List<? extends String,想要深入了解,可以看一下這里)也可以。
  • javax.annotation.processing.SupportedSourceVersion:這是用來注冊注解處理器要處理的源代碼版本。
  • javax.annotation.processing.SupportedOptions:這個(gè)注解用來注冊可能通過命令行傳遞給處理器的操作選項(xiàng)。
    (譯者語:對于android注解處理器,第一個(gè)注解比較有用,另外兩個(gè)了解就可)
    ?最后,我們提供process()方法的實(shí)現(xiàn)。

實(shí)現(xiàn)我們第一個(gè)注解處理器

讓我們開始第一個(gè)注解處理器的實(shí)現(xiàn)。按照之前章節(jié)的知識,我們實(shí)現(xiàn)了下面這個(gè)類來處理第一篇博文中的Complexity注解:

 package sdc.assets.annotations.processors;

    import …

    @SupportedAnnotationTypes("sdc.assets.annotations.Complexity")
    @SupportedSourceVersion(SourceVersion.RELEASE_6)
    public class ComplexityProcessor extends AbstractProcessor {

        public ComplexityProcessor() {
            super();
        }

        @Override
        public boolean process(Set<? extends TypeElement> annotations,
                               RoundEnvironment roundEnv) {
            return true;
        }
    }

這個(gè)未完全完成的類,盡管沒有任何操作,但是注冊可以支持處理sdc.assets.annotations.Complexity注解類型。因此,每次java編譯器遇到一個(gè)被Complexity標(biāo)記的類都有執(zhí)行這個(gè)處理器,假設(shè)這個(gè)處理器在那個(gè)路徑中可以被獲得(具體原由之后會(huì)看到)。
?process()方法會(huì)受到兩個(gè)輸入?yún)?shù):

  • Set<? extends TypeElement>:注解處理需要執(zhí)行一次或者多次。每次執(zhí)行時(shí),處理器方法被調(diào)用,并且傳入了當(dāng)前要處理的注解類型。
  • RoundEnvironment:這個(gè)對象提供當(dāng)前或者上一次注解處理中被注解標(biāo)注的源文件元素。(簡單點(diǎn)說,就是可以獲得所有被標(biāo)注的元素,無論是類,參數(shù),函數(shù)還是變量)
    ?除了上述兩個(gè)參數(shù)之外,ProcessingEnvironment對象也可以通過processingEnv實(shí)例獲得。這個(gè)對象可以提供一些關(guān)于日志,文件讀寫的通用工具類;它的一些功能之后會(huì)討論到。
    ?使用RoundEnvironment對象和Element接口的反射相關(guān)的函數(shù),我們可以實(shí)現(xiàn)一個(gè)打印Complexity標(biāo)注元素名的注解處理器。
for (Element elem :roundEnv.getElementsAnnotatedWith(Complexity.class)) {     
  Complexity complexity = elem.getAnnotation(Complexity.class);     
  String message = "annotation found in " + elem.getSimpleName()                     + " with complexity " + complexity.value();       processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message); 
  return true; // no further processing of this annotation type
}

打包并且注冊注解處理器

自定義注解處理器的最后一步就是打包并且向java編譯器獲取其他可以識別處理器的工具進(jìn)行注冊。

注冊處理器的最簡單方法就是使用標(biāo)注java服務(wù)機(jī)制:

  • 把你的注解處理器打包到j(luò)ar文件中。
  • 把jar文件加入到META-INF/services目錄。
  • 把javax.annotation.processing.Processor文件加入到目錄。
  • 把處理器的全限定名寫入一個(gè)文件中,一個(gè)處理器名一行。

java編譯器或者其他工具會(huì)搜索這個(gè)文件中標(biāo)記的所有處理器,并且在注解處理過程中使用。
?在我們這個(gè)例子中,目錄結(jié)構(gòu)和文件內(nèi)容如下:


picture1.png

picture2.png

?一旦打包完成,我們就準(zhǔn)備使用它。
(譯者語:關(guān)于android平臺(tái)的自動(dòng)打包和使用,可以參考butterknife的方法,就是使用AutoService,這個(gè)詳細(xì)知識之后我會(huì)單獨(dú)博文講解)

使用javac運(yùn)行處理器

想象一下你在一個(gè)項(xiàng)目中使用了一些自定義注解并且可以使用注解處理器。在java 5中,編譯和注解處理是不同的兩步,但是在java 6中,兩個(gè)任務(wù)都集成到j(luò)ava編譯器工具javac中。
?如果你把注解處理器加入到j(luò)avac的路徑中并且他們使用服務(wù)機(jī)制進(jìn)行了注冊,他們就會(huì)被javac執(zhí)行調(diào)用啦。
?在我們這個(gè)例子中,下邊這個(gè)命令會(huì)編譯并且執(zhí)行注解處理:

>javac -cp sdc.assets.annotations-1.0-SNAPSHOT.jar;  
sdc.assets.annotations.processors-1.0-SNAPSHOT.jar    
SimpleAnnotationsTest.java

用于處理的java類文件內(nèi)容如下:

package sdc.startupassets.annotations.base.client; 
import ... 
@Complexity(ComplexityLevel.VERY_SIMPLE) 
public class SimpleAnnotationsTest { 
  public SimpleAnnotationsTest() 
  { 
    super(); 
  } 
  @Complexity() // this annotation type applies also to methods //   the default value 'ComplexityLevel.MEDIUM' is assumed 
public void theMethod() 
  { 
    System.out.println("consoleut"); 
  } 
}

上述執(zhí)行結(jié)果如下:

picture3.png

有一些javac的選項(xiàng)可以在一些特殊的情況下使用:

  • -Akey[=value]:用來傳遞選項(xiàng)值給處理器,處理器只會(huì)接收在通過SupportedOptions注解注冊的選項(xiàng)。
  • -proc:{none|only}:默認(rèn)情況下,javac會(huì)運(yùn)行注解處理并且編譯源碼。使用proc:none選項(xiàng),將不執(zhí)行注解處理;使用proc:only選項(xiàng)將只執(zhí)行注解處理過程-當(dāng)你在注解處理器中運(yùn)行驗(yàn)證或者質(zhì)量檢查工具或者代碼審查工具時(shí)。
  • -processorpath path:用來確定注解處理器和它的依賴的位置
  • -s dir:用來確定通過注解處理生成的源代碼放置在哪個(gè)文件夾中。這個(gè)目錄在執(zhí)行命令行之前必須存在。
  • -processor class1[,class2,class3…]:用來給出將要執(zhí)行的注解處理器全限定名,當(dāng)使用這個(gè)選項(xiàng)時(shí),默認(rèn)的通過服務(wù)機(jī)制尋找到的注解處理器將被替換,直接使用命令行給出的處理器進(jìn)行處理。
    (譯者語:下邊兩個(gè)章節(jié)因?yàn)楹椭鞲蓛?nèi)容沒有太大關(guān)系,所以沒有翻譯,請感興趣的朋友自行查閱)

Eclipse 中運(yùn)行注解處理器

Maven中運(yùn)行注解處理器

最后編輯于
?著作權(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閱讀 228,835評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評論 1 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,025評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,016評論 2 375

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