日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java注解解析-搭建自己的注解处理器(CLASS注解使用篇)

發(fā)布時(shí)間:2024/1/17 java 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java注解解析-搭建自己的注解处理器(CLASS注解使用篇) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

該文章是繼Java注解解析-基礎(chǔ)+運(yùn)行時(shí)注解(RUNTIME)之后,使用注解處理器處理CLASS注解的文章。通過(guò)完整的Demo例子介紹整個(gè)注解處理器的搭建流程以及注意事項(xiàng),你將知道如何去搭建自己的注解處理器。前提是你知道如何去寫(xiě)自定義注解,不清楚的可以點(diǎn)擊我上面的鏈接哦~

介紹

顧名思義注解處理器就是javac包中專(zhuān)門(mén)用來(lái)處理注解的工具。所有的注解處理器都必須繼承抽象類(lèi)AbstractProcessor然后重寫(xiě)它的幾個(gè)方法。注解處理器是運(yùn)行在它自己的JVM中。javac啟動(dòng)一個(gè)完整Java虛擬機(jī)來(lái)運(yùn)行注解處理器,這意味著你可以使用任何你在其他java應(yīng)用中使用的的東西。其中抽象方法process是必須要重寫(xiě)的,再該方法中注解處理器可以遍歷所有的源文件,然后通過(guò)RoundEnvironment類(lèi)獲取我們需要處理的注解所標(biāo)注的所有的元素,這里的元素可以代表包,類(lèi),接口,方法,屬性等,具體的解釋放在下一章節(jié),因?yàn)檫@里面牽扯到的東西實(shí)在是太多了。再處理的過(guò)程中可以利用特定的工具類(lèi)自動(dòng)生成特定的.java文件或者.class文件,來(lái)幫助我們處理自定義注解。

下面開(kāi)始搭建

1.創(chuàng)建

首先注解處理器需要javax包的支持,我們的Android環(huán)境下是訪問(wèn)不到j(luò)avax包的,除非我們自己配置。

//app:build.gradle中加入依賴(lài),一定要使用provided files來(lái)引入. provided files('/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/jre/lib/rt.jar')

所以我們需要?jiǎng)?chuàng)建Java Library包來(lái)提供javax環(huán)境,另外注解處理器要被打包進(jìn)jar包里面才能被系統(tǒng)識(shí)別,這就是選用ava Library的原因,目前注解注解框架均是如此。

創(chuàng)建好Module之后就可以寫(xiě)我們的自定義的注解處理器了。首先需要繼承抽象類(lèi)AbstractProcessor,然后重寫(xiě)process()方法。該方法是核心方法,該方法將遍歷源代碼,找出我們想要注解標(biāo)注的元素。單單重寫(xiě)這一個(gè)方法是不夠的, 在我們的開(kāi)發(fā)中往往需要重寫(xiě)init(),getSupportedSourceVersion(),getSupportedAnnotationTypes()這幾個(gè)方法就可以了。另外再Java7及其以后我們還可以使用注解@SupportedSourceVersion()和@SupportedAnnotationTypes()?去替代上面的方法,見(jiàn)于該注解有Java版本的限制,所以還是建議直接重寫(xiě)方法為好。

public class AnnotationTwoProcessor extends AbstractProcessor {private Messager messager; //用于打印日志private Elements elementUtils; //用于處理元素private Filer filer; //用來(lái)創(chuàng)建java文件或者class文件//該方法再編譯期間會(huì)被注入一個(gè)ProcessingEnvironment對(duì)象,該對(duì)象包含了很多有用的工具類(lèi)。@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);messager = processingEnvironment.getMessager();elementUtils = processingEnvironment.getElementUtils();filer = processingEnvironment.getFiler();}/*** 該方法將一輪一輪的遍歷源代碼* @param set 該方法需要處理的注解類(lèi)型* @param roundEnvironment 關(guān)于一輪遍歷中提供給我們調(diào)用的信息.* @return 改輪注解是否處理完成 true 下輪或者其他的注解處理器將不會(huì)接收到次類(lèi)型的注解.用處不大.*/@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {return false;}/*** 返回我們Java的版本.* @return*/@Overridepublic SourceVersion getSupportedSourceVersion() {return SourceVersion.latest();}/*** 返回我們將要處理的注解* @return*/@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> annotataions = new LinkedHashSet<>();annotataions.add(MyAnnotion.class.getCanonicalName());return annotataions;} }

2.注冊(cè)

注冊(cè)注解處理器的方法有兩種:

第一種:?處理器必須被打包進(jìn)一個(gè)jar包里面,這也是為什么要建立一個(gè)Java Module。該jar包需要有一個(gè)特定路徑resources/META-INF/services的文件javax.annotation.processing.Processor,該文件的路徑和名稱(chēng)都是特定的,然后將我們聲明注解處理器的全路徑寫(xiě)到該文件中,這樣Java虛擬機(jī)會(huì)自動(dòng)找該路徑下中我們聲明的處理器。

我們?cè)賱?chuàng)建文件夾的時(shí)候一定要確定其命名的準(zhǔn)確性。創(chuàng)建文件的時(shí)候直接右鍵->new file,保證我們的文件的名字為javax.annotation.processing.Processor。?
?
創(chuàng)建成功之后我們將自定義的注解處理器的全路徑寫(xiě)到該文件里面。

這樣問(wèn)題來(lái)了,如我我們寫(xiě)了多個(gè)注解處理器該怎么聲明呢?接著看。如果我們一個(gè)Module里面聲明了多個(gè)注解處理器,再該文件中聲明的時(shí)候每個(gè)注解處理器要換行聲明,運(yùn)行的順序就按聲明的順序去執(zhí)行。這里需要對(duì)注解處理器的運(yùn)行順序解釋一下,再編譯期間并不是一個(gè)注解處理器完全的處理結(jié)束再開(kāi)始下一個(gè)的,而是在掃描一輪源代碼 的時(shí)候注冊(cè)的第一個(gè)處理器先執(zhí)行一輪,然后注冊(cè)的第二個(gè)處理器開(kāi)始執(zhí)行然后。。。三個(gè)。。四個(gè)。第二輪的時(shí)候還是注冊(cè)的第一個(gè)處理器先執(zhí)行,然后第二個(gè)。。。三個(gè)。。。這里面的深刻解釋會(huì)在下一篇講解,這篇只是使用。

cn.example.wang.processor.AnnotationProcessor cn.example.wang.processorAnnotationTwoProcessor

第二種:當(dāng)覺(jué)得第一種方法配置繁瑣的時(shí)候就會(huì)有新的更簡(jiǎn)潔的方式出現(xiàn)。谷歌公司推出的使用注解去注冊(cè)注解處理器。

  • 添加依賴(lài),可以去GitHub上面查找最新版本。

    implementation 'com.google.auto.service:auto-service:1.0-rc4'
  • 我們就可以使用了,它的作用和我們手寫(xiě)的作用是一樣的。不過(guò)注釋的處理器的處理順序跟你類(lèi)創(chuàng)建的順序是一致的,跟方法一中文件聲明的順序不一樣。

    @AutoService(Processor.class) public class AnnotationTwoProcessor extends AbstractProcessor { }
  • 總的來(lái)說(shuō)方式1的注冊(cè)方式目前僅在EventBus里面有用到,方式2還是推薦使用的,比如:Arouter,BufferKnife等流行框架都是采用的這種方式注冊(cè)的,方便快捷。

    3.APP引用注解處理器

    再引用注解處理器的Module的時(shí)候坑其實(shí)挺多的。首先我們得確保處理器所在的jar包會(huì)添加到我們運(yùn)行的項(xiàng)目中,注解處理器才會(huì)被得到執(zhí)行,這里呢因?yàn)樵创a不清楚,所以只好自己去試了。Application引入注解處理器包的時(shí)候并不像我們引入其它的Android Module一樣,這里列舉三種種引入方法。

    • plugin: 'com.android.application'

      我們可以使用implementation,compile,api直接引用注解處理器的module,但是會(huì)有一個(gè)編譯錯(cuò)誤:

      Error:Execution failed for task ':app:javaPreCompileDebug'. > Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor. Please add them to the annotationProcessor configuration.- annotation_processor.jar (project :annotation_processor) Alternatively, set android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true to continue with previous behavior. Note that this option is deprecated and will be removed in the future. See https://developer.android.com/r/tools/annotation-processor-error-message.html for more details.

      我們需要加一段代碼,代碼位置如下所示,這樣就可以愉快的引入注解處理器了:

      android {defaultConfig {javaCompileOptions {annotationProcessorOptions {includeCompileClasspath = true}}} }

      另外我們可以用annotationProcessor引入注解處理器,這個(gè)引入方式是為了替換Gradle版本2.2之前的apt引入方法。該引入方式專(zhuān)門(mén)用來(lái)處理注解處理器。使用該引入方式的時(shí)候不會(huì)出現(xiàn)錯(cuò)誤提示,也是Android中推薦使用的引入方法,該方式也是主流方式。

      annotationProcessor project(':annotation_processor')
    • apply plugin: 'com.android.library'

      前提module一定要被android application引入。module里面引入注解處理器的module的話,基本跟android application中一致,這里說(shuō)一下兩個(gè)不同點(diǎn)annotationProcessor和implementation方式的引入都不會(huì)執(zhí)行注解處理器,真實(shí)的原理并不清楚,只能猜測(cè)是application才能正真的處理注解,所以得把依賴(lài)暴露出去。這點(diǎn)再android library的module中一定得注意。不太建議該方式引入。

    • apply plugin: 'java-library'

      前提java library一定要被android application引入。聲明這樣的module 的話我們就可以直接引入注解處理器了,也不用擔(dān)心用什么方式引入。不過(guò)這種場(chǎng)景不太多。

    如何確保你的注解處理器已經(jīng)注冊(cè)成功了。首先你已經(jīng)自定義好了一個(gè)注解,并且該注解已經(jīng)用到了你的代碼里面。如下代碼所示,你已經(jīng)設(shè)置了我們要處理的是那種類(lèi)型的注解(代碼1所示),然后再我們的process方法里面打上日志(代碼2所示),然后點(diǎn)擊Android Studio的Make Project按鈕,之后打開(kāi)Gradle Console窗口看build信息,當(dāng)你發(fā)現(xiàn)信息中打印了代碼2所示的信息之后就表明你的注解處理器已經(jīng)運(yùn)行起來(lái)了。如果沒(méi)有打印信息的話嘗試 clean一下項(xiàng)目然后重新Make Project。如果發(fā)現(xiàn)沒(méi)有打印日志的話,嘗試查看注解處理器是否已經(jīng)注冊(cè)和注解處理器所在的module是否被android application成功引入。

    @AutoService(Processor.class) public class AnnotationProcessor extends AbstractProcessor {private Messager messager;@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);messager = processingEnvironment.getMessager();}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {//代碼2messager.printMessage(Diagnostic.Kind.NOTE,"日志開(kāi)始---------------");return false;}//代碼1@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> supportedOptions = new HashSet<>();supportedOptions.add(MyAnnotion.class.getCanonicalName());return supportedOptions;}@Overridepublic SourceVersion getSupportedSourceVersion() {return SourceVersion.latest();} }

    4.自動(dòng)生成Java文件

    走到該步驟的時(shí)候,你要確保你的注解處理器已經(jīng)正常的運(yùn)行。我們使用注解處理器處理源碼注解的目的,就是再編譯期間完成我們對(duì)某個(gè)注解的處理工作。比如:BufferKnife再編譯期間會(huì)在使用特定注解的文件路徑生成*—ViewBinding的源文件去處理特定注解。這里用一個(gè)Demo去演示如何生成代碼:

    先看我的注解:

    @Retention(RetentionPolicy.CLASS) @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.LOCAL_VARIABLE}) public @interface MyAnnotion {String value() default "ssssss";}

    在我的MainActivity上面使用注解:

    @MyAnnotion() public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);} }

    接著使用注解處理器去處理注解,生成對(duì)應(yīng)的MainActivity_ViewBinding源文件。

    public class AnnotationProcessor extends AbstractProcessor {private Messager messager;private Elements elementUtils;private Filer filer;@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);messager = processingEnvironment.getMessager();elementUtils = processingEnvironment.getElementUtils();filer = processingEnvironment.getFiler();}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {messager.printMessage(Diagnostic.Kind.NOTE,"日志開(kāi)始---------------");Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(MyAnnotion.class);for (Element element:elementsAnnotatedWith) {if(element.getKind() == ElementKind.CLASS){TypeElement typeElement = (TypeElement) element;PackageElement packageElement = elementUtils.getPackageOf(element);String packagePath = packageElement.getQualifiedName().toString();String className = typeElement.getSimpleName().toString();try {JavaFileObject sourceFile = filer.createSourceFile(packagePath + "." + className + "_ViewBinding", typeElement);Writer writer = sourceFile.openWriter();writer.write("package "+packagePath +";\n");writer.write("import "+packagePath+"."+className+";\n");writer.write("public class "+className+"_ViewBinding"+" { \n");writer.write("\n");writer.append(" public "+className +" targe;\n");writer.write("\n");writer.append("}");writer.flush();writer.close();} catch (IOException e) {e.printStackTrace();}}}messager.printMessage(Diagnostic.Kind.NOTE,"日志結(jié)束---------------");return false;}@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> supportedOptions = new HashSet<>();supportedOptions.add(MyAnnotion.class.getCanonicalName());return supportedOptions;}@Overridepublic SourceVersion getSupportedSourceVersion() {return SourceVersion.latest();} }

    結(jié)果展示:[圖片上傳失敗…(image-c3199d-1536119413375)]

    注意一下生成的位置!我們可以直接再我們正常的代碼中應(yīng)用到該文件,因?yàn)樵撐募菚?huì)生成class文件的。

    5.總結(jié)

    該文章只是介紹了如何搭建起一個(gè)Java注解處理器,沒(méi)有更深入的去講解AbstractProcessor類(lèi)以及我們?cè)偬幚碜⒔獾倪^(guò)程中用到的各種類(lèi)的API。當(dāng)然接下來(lái)的文章就會(huì)詳細(xì)的介紹注解處理器所使用到的類(lèi),方法,屬性等的用法和意義,這一定是史上最全的注解處理器API。之后你會(huì)更加隨心所欲的去構(gòu)建自己的注解框架。

    總結(jié)

    以上是生活随笔為你收集整理的Java注解解析-搭建自己的注解处理器(CLASS注解使用篇)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。