溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Annotation Processor 處理器問題如何深度定位

發(fā)布時間:2022-01-12 17:53:38 來源:億速云 閱讀:141 作者:柒染 欄目:服務器

Annotation Processor 處理器問題如何深度定位,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

什么是 Annotation Processor 構建問題

寫過自定義注解處理器的老司機們乍一看這個問題覺得挺簡單,是的,因為網(wǎng)上基本通篇都在教你怎么打日志,但是你有沒有想過如果連日志都打印不出來的時候你怎么定位呢?譬如如下代碼:

// 確認 META-INF/services/javax.annotation.processing.Processor 沒問題 // 確認構建腳本沒問題,確認注解 Bridge 有被使用且有參與構建 @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;     } }

運行構建后compileReleaseJavaWithJavac過程中沒有先吐我 Annotation Processor  的任意一行日志,直接報錯找不到我注解處理器產(chǎn)物類引用(即直接進行了 compile class 環(huán)節(jié))。

你懵逼嗎?反正我懵逼了!打印日志不好使了,哈哈,環(huán)境確認沒問題,什么鬼,直接越過 Annotation Processor 進行 compile  了。

這時候就需要你稍微深入定位分析(擼javac源碼的巨佬請自行飄過),前提就是你需要熟悉下 Annotation Processor  基本原理,然后我們通過一些額外的javac詳細日志進行舉例分析。

Annotation Processor 機制

注解和注解處理器是 JDK5 引入的機制,主要用來為類、方法、字段和參數(shù)等 Java 結構提供額外的信息。譬如常見的@Override就是僅僅對 Java  編譯器生效的一個注解。Java  允許我們自定義注解,自定義的注解處理器就是用來處理這些自定義注解的(廢話),注解處理器觸發(fā)時機是由javac來處理的,所以整個javac過程的簡要步驟如下圖:

Annotation Processor 處理器問題如何深度定位

javac 主要步驟

可以看到,javac編譯概要圖主要分為如下幾步:

  1. 把源文件解析為抽象語法樹。

  2. 調(diào)用已注冊的注解處理器。

  3. 如果注解處理器處理過程中生成了新的源文件,編譯器重復第 1、2 步,當注解處理器不再生成新的源文件則進入最后一輪。

  4. 進入真正的 compile 字節(jié)碼環(huán)節(jié)生成字節(jié)碼。

如上就是注解處理器的核心機制,有了這個核心機制的認識我們就繼續(xù)往下探索。

構建工具下 Annotation Processor 的本質(zhì)

我們?nèi)粘i_發(fā)中(無論是 Java 后端還是 Android 移動端)總是多多少少會用到 JDK 提供的annotation  processor能力,無論是什么構建工具(Gradle 或者 Maven 等)本質(zhì)都是通過javac -processorpath命令參數(shù)顯式指定哪些  Processer,或者顯式聲明META-INF/services/javax.annotation.processing.Processor來被javac發(fā)現(xiàn)并調(diào)用的(參見  google 的 AutoService 框架)。

正常情況下我們開發(fā)中使用及構建 Annotation Processor 技術都是上面幾步走的方案,而且大多數(shù)照著網(wǎng)絡上抄的都能正常工作,每次只用自己處理  process 就挺香的,因為只要按照規(guī)則聲明放置,其他的 javac都能自己完美調(diào)用。

增強 javac 過程打印暴露問題

要解決一開始說的 Annotation Processor  中自己加的日志都不打印場景問題,我們需要獲取一些額外的信息輔助定位。由于直接使用命令行javac的方式是最原始的操作,我們構建一般采用 Gradle,而  Gradle 的本質(zhì)還是調(diào)用javac,所以下面我們以 Gradle 為例來分析如何定位 Annotation Processor 問題。

下面簡單粗暴點就是直接在根目錄的build.gradle中給所有模塊添加參數(shù):

// 參數(shù)可選,重點是 -verbose -XprintRounds -XprintProcessorInfo allprojects {     gradle.projectsEvaluated {         tasks.withType(JavaCompile) {             options.compilerArgs << "-Xlint" << "-verbose" << "-XprintRounds" << "-XprintProcessorInfo" << "-Xmaxerrs" << "100000"         }     } }

你也可以僅僅在自己有注解處理器的模塊中添加,與上面一樣,只要加給JavaCompile的參數(shù)就行。這里的參數(shù)其實就是我們平時命令行javac是否的參數(shù),不懂的可以去命令行執(zhí)行下javac  -help觀摩下含義吧,如下(JDK8,不同版本 JDK 略有差異):

yan@yanDeMackbookPro:~$ javac -help 用法: javac <options> <source files> 其中, 可能的選項包括: -g                         生成所有調(diào)試信息 ...... -verbose                   輸出有關編譯器正在執(zhí)行的操作的消息 ...... -processor <class1>[,<class2>,<class3>...] 要運行的注釋處理程序的名稱; 繞過默認的搜索進程 -processorpath <路徑>        指定查找注釋處理程序的位置 ......

至于腳本中其他幾個在javac  -help中沒有的參數(shù)可以看下官方文檔https://docs.oracle.com/en/java/javase/11/tools/javac.html  ,里面詳細解釋了參數(shù)含義。

添加上面參數(shù)后一定要將你的構建日志追加到一個磁盤文件中,因為日志會變得非常龐大,同時也變得很容易定位問題。

通過構建日志分析定位問題

執(zhí)行你的構建任務,完畢后分析定位主要分為如下幾個步驟,每一步都是一種場景的定位,循序漸進定位分析即可。

1.在你的日志中搜索你的 Processor 類名,譬如TestAnnotationProcessor.class,看到的日志會是如下。

// 如果你的注解處理器在項目中是源碼形式的日志 [loading RegularFileObject[/home/user/yan/test/target/classes/cn/yan/test/TestAnnotationProcessor.class]]  // 如果你的注解處理器在項目中是依賴 jar 形式的日志 [loading ZipFileIndexFileObject[....../test.jar(cn/yan/test/TestAnnotationProcessor.class)]]

分析: 如果你的日志中搜不到上面信息,說明你的注解處理器沒有被添加到javac的 classpath  中。一般問題就是你的META-INF/services/javax.annotation.processing.Processor聲明有問題,javac無法找到你的注解處理器。有些同學可能是通過  google 的 AutoService  來生成META-INF/services/javax.annotation.processing.Processor的,這種情況下也要自己檢查是否  OK(譬如之前安卓中 AGP 有一段時間的中間過渡版本就修改了 classpath,需要手動將 compile 改成 annotationProcessor  才行)。

2.在你的日志中搜索Round關鍵字,建議直接搜Round 1:這樣的格式容易點,看到的日志會是如下。

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),則日志如上才是正常的。

分析:  如果你日志中沒搜到上面的Round,則說明javac沒有觸發(fā)調(diào)用任何注解處理器(無論是你寫的還是依賴三方框架的),最大的可疑點就是檢查下自己有沒有禁用javac注解處理器,也就是確認javac執(zhí)行時沒有-proc:none參數(shù)。如果你的日志中有Round,但是input  files:和annotations:沒有你的注解類和使用類,則說明你沒有在代碼中使用你注解處理器要處理的注解。

3.在你的日志中搜索Loaded cn.yan.test.TestAnnotationProcessor關鍵字,看到的日志會是如下。

[Loaded cn.yan.test.TestAnnotationProcessor from file:/home/user/yan/test/target/classes/cn/yan/test/TestAnnotationProcessor.class]

分析:  如果你看不到上面這行日志,說明你的注解處理器類自己沒有被加載成功,為什么沒有我也不知道怎么分析了,但是至少說明沒加載成功,你可能需要仔細核對哪里不規(guī)范或者不合法導致的了。

上面都排查完了,如果還是找不到問題原因,不妨換個思路,去仔細檢查下你參與構建的普通 java  文件,是否存在語法錯誤或者什么問題(譬如常量沒聲明等);如果有,解決完了再試試,別問我為什么,我也沒有深入研究javac這塊源碼,只是我遇到過,且也沒有異常堆棧信息,最終發(fā)現(xiàn)是合并解決沖突后代碼少了一個變量聲明,就是單純的越過了  Annotation Processor 過程直接進行 compile to class 流程了)。

這個技能有什么鳥用?

不瞞你說,我也算是老司機了,好些年前 Annotation Processor 就玩的很 6 了,但是最近項目升級構建和 Java8 及 androidX  支持后 merge 了下代碼,然后項目中的注解處理器、dataBinding  全部都不工作了,更可氣的是,這個不工作是真的很吝嗇,什么錯誤堆棧都沒有,大致如下奇葩構建日志:

FAILURE: Build failed with an exception.  * What went wrong: Execution failed for task ':test:compileReleaseJavaWithJavac'. // 本來這里該先吐我注解處理器內(nèi)部的日志,然后才繼續(xù) javac 編譯,實際什么都沒吐 > 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  構建命令已經(jīng)添加了各種詳細參數(shù)供查看堆棧和詳細日志,但奇妙的事情就是他走到compileReleaseJavaWithJavac就直接出錯了,前后沒有任何錯誤提示(有的只是一坨  Gradle 自己的 task 調(diào)用鏈)。我特么大意了,我就同步了下代碼,編不過就編不過啊,你倒是提示下問題啊!啥也不提示直接干到 compile class  環(huán)節(jié)了,跳過了 Annotation Processor 流程,這就很惱火了。好在按照上面方式定位修復了,哈哈。

關于Annotation Processor 處理器問題如何深度定位問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業(yè)資訊頻道了解更多相關知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI