Annotation Processor 處理器問(wèn)題如何深度定位,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
專(zhuān)注于為中小企業(yè)提供網(wǎng)站制作、網(wǎng)站設(shè)計(jì)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)石拐免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了超過(guò)千家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
什么是 Annotation Processor 構(gòu)建問(wèn)題
寫(xiě)過(guò)自定義注解處理器的老司機(jī)們乍一看這個(gè)問(wèn)題覺(jué)得挺簡(jiǎn)單,是的,因?yàn)榫W(wǎng)上基本通篇都在教你怎么打日志,但是你有沒(méi)有想過(guò)如果連日志都打印不出來(lái)的時(shí)候你怎么定位呢?譬如如下代碼:
// 確認(rèn) META-INF/services/javax.annotation.processing.Processor 沒(méi)問(wèn)題 // 確認(rèn)構(gòu)建腳本沒(méi)問(wèn)題,確認(rèn)注解 Bridge 有被使用且有參與構(gòu)建 @AutoService(Processor.class) public class TestAnnotationProcessor extends AbstractProcessor { public TestAnnotationProcessor() { System.out.println("TestAnnotationProcessor constrator"); } @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); System.out.println("TestAnnotationProcessor init"); } @Override public Set<String> getSupportedAnnotationTypes() { System.out.println("TestAnnotationProcessor getSupportedAnnotationTypes"); Set<String> supported = new HashSet<String>(); supported.add(Bridge.class.getCanonicalName()); return supported; } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { System.out.println("TestAnnotationProcessor process"); return true; } }運(yùn)行構(gòu)建后compileReleaseJavaWithJavac過(guò)程中沒(méi)有先吐我 Annotation Processor 的任意一行日志,直接報(bào)錯(cuò)找不到我注解處理器產(chǎn)物類(lèi)引用(即直接進(jìn)行了 compile class 環(huán)節(jié))。
你懵逼嗎?反正我懵逼了!打印日志不好使了,哈哈,環(huán)境確認(rèn)沒(méi)問(wèn)題,什么鬼,直接越過(guò) Annotation Processor 進(jìn)行 compile 了。
這時(shí)候就需要你稍微深入定位分析(擼javac源碼的巨佬請(qǐng)自行飄過(guò)),前提就是你需要熟悉下 Annotation Processor 基本原理,然后我們通過(guò)一些額外的javac詳細(xì)日志進(jìn)行舉例分析。
Annotation Processor 機(jī)制
注解和注解處理器是 JDK5 引入的機(jī)制,主要用來(lái)為類(lèi)、方法、字段和參數(shù)等 Java 結(jié)構(gòu)提供額外的信息。譬如常見(jiàn)的@Override就是僅僅對(duì) Java 編譯器生效的一個(gè)注解。Java 允許我們自定義注解,自定義的注解處理器就是用來(lái)處理這些自定義注解的(廢話),注解處理器觸發(fā)時(shí)機(jī)是由javac來(lái)處理的,所以整個(gè)javac過(guò)程的簡(jiǎn)要步驟如下圖:

javac 主要步驟
可以看到,javac編譯概要圖主要分為如下幾步:
把源文件解析為抽象語(yǔ)法樹(shù)。
調(diào)用已注冊(cè)的注解處理器。
如果注解處理器處理過(guò)程中生成了新的源文件,編譯器重復(fù)第 1、2 步,當(dāng)注解處理器不再生成新的源文件則進(jìn)入最后一輪。
進(jìn)入真正的 compile 字節(jié)碼環(huán)節(jié)生成字節(jié)碼。
如上就是注解處理器的核心機(jī)制,有了這個(gè)核心機(jī)制的認(rèn)識(shí)我們就繼續(xù)往下探索。
構(gòu)建工具下 Annotation Processor 的本質(zhì)
我們?nèi)粘i_(kāi)發(fā)中(無(wú)論是 Java 后端還是 Android 移動(dòng)端)總是多多少少會(huì)用到 JDK 提供的annotation processor能力,無(wú)論是什么構(gòu)建工具(Gradle 或者 Maven 等)本質(zhì)都是通過(guò)javac -processorpath命令參數(shù)顯式指定哪些 Processer,或者顯式聲明META-INF/services/javax.annotation.processing.Processor來(lái)被javac發(fā)現(xiàn)并調(diào)用的(參見(jiàn) google 的 AutoService 框架)。
正常情況下我們開(kāi)發(fā)中使用及構(gòu)建 Annotation Processor 技術(shù)都是上面幾步走的方案,而且大多數(shù)照著網(wǎng)絡(luò)上抄的都能正常工作,每次只用自己處理 process 就挺香的,因?yàn)橹灰凑找?guī)則聲明放置,其他的 javac都能自己完美調(diào)用。
增強(qiáng) javac 過(guò)程打印暴露問(wèn)題
要解決一開(kāi)始說(shuō)的 Annotation Processor 中自己加的日志都不打印場(chǎng)景問(wèn)題,我們需要獲取一些額外的信息輔助定位。由于直接使用命令行javac的方式是最原始的操作,我們構(gòu)建一般采用 Gradle,而 Gradle 的本質(zhì)還是調(diào)用javac,所以下面我們以 Gradle 為例來(lái)分析如何定位 Annotation Processor 問(wèn)題。
下面簡(jiǎn)單粗暴點(diǎn)就是直接在根目錄的build.gradle中給所有模塊添加參數(shù):
// 參數(shù)可選,重點(diǎn)是 -verbose -XprintRounds -XprintProcessorInfo allprojects { gradle.projectsEvaluated { tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint" << "-verbose" << "-XprintRounds" << "-XprintProcessorInfo" << "-Xmaxerrs" << "100000" } } }你也可以?xún)H僅在自己有注解處理器的模塊中添加,與上面一樣,只要加給JavaCompile的參數(shù)就行。這里的參數(shù)其實(shí)就是我們平時(shí)命令行javac是否的參數(shù),不懂的可以去命令行執(zhí)行下javac -help觀摩下含義吧,如下(JDK8,不同版本 JDK 略有差異):
yan@yanDeMackbookPro:~$ javac -help 用法: javac <options> <source files> 其中, 可能的選項(xiàng)包括: -g 生成所有調(diào)試信息 ...... -verbose 輸出有關(guān)編譯器正在執(zhí)行的操作的消息 ...... -processor <class1>[,<class2>,<class3>...] 要運(yùn)行的注釋處理程序的名稱(chēng); 繞過(guò)默認(rèn)的搜索進(jìn)程 -processorpath <路徑> 指定查找注釋處理程序的位置 ......
至于腳本中其他幾個(gè)在javac -help中沒(méi)有的參數(shù)可以看下官方文檔https://docs.oracle.com/en/java/javase/11/tools/javac.html ,里面詳細(xì)解釋了參數(shù)含義。
添加上面參數(shù)后一定要將你的構(gòu)建日志追加到一個(gè)磁盤(pán)文件中,因?yàn)槿罩緯?huì)變得非常龐大,同時(shí)也變得很容易定位問(wèn)題。
通過(guò)構(gòu)建日志分析定位問(wèn)題
執(zhí)行你的構(gòu)建任務(wù),完畢后分析定位主要分為如下幾個(gè)步驟,每一步都是一種場(chǎng)景的定位,循序漸進(jìn)定位分析即可。
1.在你的日志中搜索你的 Processor 類(lèi)名,譬如TestAnnotationProcessor.class,看到的日志會(huì)是如下。
// 如果你的注解處理器在項(xiàng)目中是源碼形式的日志 [loading RegularFileObject[/home/user/yan/test/target/classes/cn/yan/test/TestAnnotationProcessor.class]] // 如果你的注解處理器在項(xiàng)目中是依賴(lài) jar 形式的日志 [loading ZipFileIndexFileObject[....../test.jar(cn/yan/test/TestAnnotationProcessor.class)]]
分析: 如果你的日志中搜不到上面信息,說(shuō)明你的注解處理器沒(méi)有被添加到j(luò)avac的 classpath 中。一般問(wèn)題就是你的META-INF/services/javax.annotation.processing.Processor聲明有問(wèn)題,javac無(wú)法找到你的注解處理器。有些同學(xué)可能是通過(guò) google 的 AutoService 來(lái)生成META-INF/services/javax.annotation.processing.Processor的,這種情況下也要自己檢查是否 OK(譬如之前安卓中 AGP 有一段時(shí)間的中間過(guò)渡版本就修改了 classpath,需要手動(dòng)將 compile 改成 annotationProcessor 才行)。
2.在你的日志中搜索Round關(guān)鍵字,建議直接搜Round 1:這樣的格式容易點(diǎn),看到的日志會(huì)是如下。
Round 1: input files: {cn.yan.test.Application, ......, cn.yan.test.UseMarkedAnnotation} annotations: [java.lang.Override, cn.yan.annotation.Bridge] last round: false上面日志中的input files:部分是掃到的你的源碼,annotations:部分就是掃到你代碼中使用了哪些注解,如果你注解處理器聲明了要處理這種注解(譬如@cn.yan.annotation.Bridge),則日志如上才是正常的。
分析: 如果你日志中沒(méi)搜到上面的Round,則說(shuō)明javac沒(méi)有觸發(fā)調(diào)用任何注解處理器(無(wú)論是你寫(xiě)的還是依賴(lài)三方框架的),最大的可疑點(diǎn)就是檢查下自己有沒(méi)有禁用javac注解處理器,也就是確認(rèn)javac執(zhí)行時(shí)沒(méi)有-proc:none參數(shù)。如果你的日志中有Round,但是input files:和annotations:沒(méi)有你的注解類(lèi)和使用類(lèi),則說(shuō)明你沒(méi)有在代碼中使用你注解處理器要處理的注解。
3.在你的日志中搜索Loaded cn.yan.test.TestAnnotationProcessor關(guān)鍵字,看到的日志會(huì)是如下。
[Loaded cn.yan.test.TestAnnotationProcessor from file:/home/user/yan/test/target/classes/cn/yan/test/TestAnnotationProcessor.class]
分析: 如果你看不到上面這行日志,說(shuō)明你的注解處理器類(lèi)自己沒(méi)有被加載成功,為什么沒(méi)有我也不知道怎么分析了,但是至少說(shuō)明沒(méi)加載成功,你可能需要仔細(xì)核對(duì)哪里不規(guī)范或者不合法導(dǎo)致的了。
上面都排查完了,如果還是找不到問(wèn)題原因,不妨換個(gè)思路,去仔細(xì)檢查下你參與構(gòu)建的普通 java 文件,是否存在語(yǔ)法錯(cuò)誤或者什么問(wèn)題(譬如常量沒(méi)聲明等);如果有,解決完了再試試,別問(wèn)我為什么,我也沒(méi)有深入研究javac這塊源碼,只是我遇到過(guò),且也沒(méi)有異常堆棧信息,最終發(fā)現(xiàn)是合并解決沖突后代碼少了一個(gè)變量聲明,就是單純的越過(guò)了 Annotation Processor 過(guò)程直接進(jìn)行 compile to class 流程了)。
這個(gè)技能有什么鳥(niǎo)用?
不瞞你說(shuō),我也算是老司機(jī)了,好些年前 Annotation Processor 就玩的很 6 了,但是最近項(xiàng)目升級(jí)構(gòu)建和 Java8 及 androidX 支持后 merge 了下代碼,然后項(xiàng)目中的注解處理器、dataBinding 全部都不工作了,更可氣的是,這個(gè)不工作是真的很吝嗇,什么錯(cuò)誤堆棧都沒(méi)有,大致如下奇葩構(gòu)建日志:
FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':test:compileReleaseJavaWithJavac'. // 本來(lái)這里該先吐我注解處理器內(nèi)部的日志,然后才繼續(xù) javac 編譯,實(shí)際什么都沒(méi)吐 > Compilation failed; see the compiler error output for details. * Exception is: org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':moffice:compileReleaseJavaWithJavac'. at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:200) ...... Caused by: org.gradle.api.internal.tasks.compile.CompilationFailedException: Compilation failed; see the compiler error output for details. at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:57)
Gradle 構(gòu)建命令已經(jīng)添加了各種詳細(xì)參數(shù)供查看堆棧和詳細(xì)日志,但奇妙的事情就是他走到compileReleaseJavaWithJavac就直接出錯(cuò)了,前后沒(méi)有任何錯(cuò)誤提示(有的只是一坨 Gradle 自己的 task 調(diào)用鏈)。我特么大意了,我就同步了下代碼,編不過(guò)就編不過(guò)啊,你倒是提示下問(wèn)題啊!啥也不提示直接干到 compile class 環(huán)節(jié)了,跳過(guò)了 Annotation Processor 流程,這就很惱火了。好在按照上面方式定位修復(fù)了,哈哈。
關(guān)于Annotation Processor 處理器問(wèn)題如何深度定位問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
新聞名稱(chēng):AnnotationProcessor處理器問(wèn)題如何深度定位
文章源于:http://chinadenli.net/article10/jggcdo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)、云服務(wù)器、網(wǎng)站策劃、用戶(hù)體驗(yàn)、網(wǎng)站維護(hù)、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)