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

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

介紹

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

  • 在運(yùn)行時(shí)刻,帶有runtime retention policy的注解可以通過(guò)反射獲得,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開始,注解處理通過(guò)JSR 269(2)被標(biāo)準(zhǔn)化,被集成進(jìn)標(biāo)準(zhǔn)庫(kù)并且apt無(wú)縫的集成到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è)注解來(lái)配置自己:

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

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

讓我們開始第一個(gè)注解處理器的實(shí)現(xiàn)。按照之前章節(jié)的知識(shí),我們實(shí)現(xiàn)了下面這個(gè)類來(lái)處理第一篇博文中的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è)未完全完成的類,盡管沒有任何操作,但是注冊(cè)可以支持處理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è)對(duì)象提供當(dāng)前或者上一次注解處理中被注解標(biāo)注的源文件元素。(簡(jiǎn)單點(diǎn)說(shuō),就是可以獲得所有被標(biāo)注的元素,無(wú)論是類,參數(shù),函數(shù)還是變量)
    ?除了上述兩個(gè)參數(shù)之外,ProcessingEnvironment對(duì)象也可以通過(guò)processingEnv實(shí)例獲得。這個(gè)對(duì)象可以提供一些關(guān)于日志,文件讀寫的通用工具類;它的一些功能之后會(huì)討論到。
    ?使用RoundEnvironment對(duì)象和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
}

打包并且注冊(cè)注解處理器

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

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

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

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


picture1.png

picture2.png

?一旦打包完成,我們就準(zhǔn)備使用它。
(譯者語(yǔ):關(guān)于android平臺(tái)的自動(dòng)打包和使用,可以參考butterknife的方法,就是使用AutoService,這個(gè)詳細(xì)知識(shí)之后我會(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)行了注冊(cè),他們就會(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]:用來(lái)傳遞選項(xiàng)值給處理器,處理器只會(huì)接收在通過(guò)SupportedOptions注解注冊(cè)的選項(xiàng)。
  • -proc:{none|only}:默認(rèn)情況下,javac會(huì)運(yùn)行注解處理并且編譯源碼。使用proc:none選項(xiàng),將不執(zhí)行注解處理;使用proc:only選項(xiàng)將只執(zhí)行注解處理過(guò)程-當(dāng)你在注解處理器中運(yùn)行驗(yàn)證或者質(zhì)量檢查工具或者代碼審查工具時(shí)。
  • -processorpath path:用來(lái)確定注解處理器和它的依賴的位置
  • -s dir:用來(lái)確定通過(guò)注解處理生成的源代碼放置在哪個(gè)文件夾中。這個(gè)目錄在執(zhí)行命令行之前必須存在。
  • -processor class1[,class2,class3…]:用來(lái)給出將要執(zhí)行的注解處理器全限定名,當(dāng)使用這個(gè)選項(xiàng)時(shí),默認(rèn)的通過(guò)服務(wù)機(jī)制尋找到的注解處理器將被替換,直接使用命令行給出的處理器進(jìn)行處理。
    (譯者語(yǔ):下邊兩個(gè)章節(jié)因?yàn)楹椭鞲蓛?nèi)容沒有太大關(guān)系,所以沒有翻譯,請(qǐng)感興趣的朋友自行查閱)

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

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

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來(lái) What:A...
    zlcook閱讀 29,756評(píng)論 15 116
  • Java 中的注解(Annotation) 是一個(gè)很方便的特性在Spring當(dāng)中得到了大量的應(yīng)用 , 我們也可以開...
    _秋天閱讀 10,166評(píng)論 3 22
  • 整體Retrofit內(nèi)容如下: 1、Retrofit解析1之前哨站——理解RESTful 2、Retrofit解析...
    隔壁老李頭閱讀 8,821評(píng)論 4 31
  • 前面寫了Android 開發(fā):由模塊化到組件化(一),很多小伙伴來(lái)問(wèn)怎么沒有Demo啊?之所以沒有立刻放demo的...
    涅槃1992閱讀 8,221評(píng)論 4 37
  • 在小巷的盡頭,有一戶貧窮的人家,他們住著破舊的房子,過(guò)著平淡的生活。因?yàn)槭抢蟻?lái)得子,對(duì)遲來(lái)的兒子疼愛有加。但那...
    關(guān)山風(fēng)月閱讀 330評(píng)論 0 0

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