您好,登錄后才能下訂單哦!
Annotation Processor 處理器問題如何深度定位,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
什么是 Annotation Processor 構建問題
寫過自定義注解處理器的老司機們乍一看這個問題覺得挺簡單,是的,因為網上基本通篇都在教你怎么打日志,但是你有沒有想過如果連日志都打印不出來的時候你怎么定位呢?譬如如下代碼:
// 確認 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 的任意一行日志,直接報錯找不到我注解處理器產物類引用(即直接進行了 compile class 環節)。
你懵逼嗎?反正我懵逼了!打印日志不好使了,哈哈,環境確認沒問題,什么鬼,直接越過 Annotation Processor 進行 compile 了。
這時候就需要你稍微深入定位分析(擼javac源碼的巨佬請自行飄過),前提就是你需要熟悉下 Annotation Processor 基本原理,然后我們通過一些額外的javac詳細日志進行舉例分析。
Annotation Processor 機制
注解和注解處理器是 JDK5 引入的機制,主要用來為類、方法、字段和參數等 Java 結構提供額外的信息。譬如常見的@Override就是僅僅對 Java 編譯器生效的一個注解。Java 允許我們自定義注解,自定義的注解處理器就是用來處理這些自定義注解的(廢話),注解處理器觸發時機是由javac來處理的,所以整個javac過程的簡要步驟如下圖:
javac 主要步驟
可以看到,javac編譯概要圖主要分為如下幾步:
把源文件解析為抽象語法樹。
調用已注冊的注解處理器。
如果注解處理器處理過程中生成了新的源文件,編譯器重復第 1、2 步,當注解處理器不再生成新的源文件則進入最后一輪。
進入真正的 compile 字節碼環節生成字節碼。
如上就是注解處理器的核心機制,有了這個核心機制的認識我們就繼續往下探索。
構建工具下 Annotation Processor 的本質
我們日常開發中(無論是 Java 后端還是 Android 移動端)總是多多少少會用到 JDK 提供的annotation processor能力,無論是什么構建工具(Gradle 或者 Maven 等)本質都是通過javac -processorpath命令參數顯式指定哪些 Processer,或者顯式聲明META-INF/services/javax.annotation.processing.Processor來被javac發現并調用的(參見 google 的 AutoService 框架)。
正常情況下我們開發中使用及構建 Annotation Processor 技術都是上面幾步走的方案,而且大多數照著網絡上抄的都能正常工作,每次只用自己處理 process 就挺香的,因為只要按照規則聲明放置,其他的 javac都能自己完美調用。
增強 javac 過程打印暴露問題
要解決一開始說的 Annotation Processor 中自己加的日志都不打印場景問題,我們需要獲取一些額外的信息輔助定位。由于直接使用命令行javac的方式是最原始的操作,我們構建一般采用 Gradle,而 Gradle 的本質還是調用javac,所以下面我們以 Gradle 為例來分析如何定位 Annotation Processor 問題。
下面簡單粗暴點就是直接在根目錄的build.gradle中給所有模塊添加參數:
// 參數可選,重點是 -verbose -XprintRounds -XprintProcessorInfo allprojects { gradle.projectsEvaluated { tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint" << "-verbose" << "-XprintRounds" << "-XprintProcessorInfo" << "-Xmaxerrs" << "100000" } } }
你也可以僅僅在自己有注解處理器的模塊中添加,與上面一樣,只要加給JavaCompile的參數就行。這里的參數其實就是我們平時命令行javac是否的參數,不懂的可以去命令行執行下javac -help觀摩下含義吧,如下(JDK8,不同版本 JDK 略有差異):
yan@yanDeMackbookPro:~$ javac -help 用法: javac <options> <source files> 其中, 可能的選項包括: -g 生成所有調試信息 ...... -verbose 輸出有關編譯器正在執行的操作的消息 ...... -processor <class1>[,<class2>,<class3>...] 要運行的注釋處理程序的名稱; 繞過默認的搜索進程 -processorpath <路徑> 指定查找注釋處理程序的位置 ......
至于腳本中其他幾個在javac -help中沒有的參數可以看下官方文檔https://docs.oracle.com/en/java/javase/11/tools/javac.html ,里面詳細解釋了參數含義。
添加上面參數后一定要將你的構建日志追加到一個磁盤文件中,因為日志會變得非常龐大,同時也變得很容易定位問題。
通過構建日志分析定位問題
執行你的構建任務,完畢后分析定位主要分為如下幾個步驟,每一步都是一種場景的定位,循序漸進定位分析即可。
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沒有觸發調用任何注解處理器(無論是你寫的還是依賴三方框架的),最大的可疑點就是檢查下自己有沒有禁用javac注解處理器,也就是確認javac執行時沒有-proc:none參數。如果你的日志中有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]
分析: 如果你看不到上面這行日志,說明你的注解處理器類自己沒有被加載成功,為什么沒有我也不知道怎么分析了,但是至少說明沒加載成功,你可能需要仔細核對哪里不規范或者不合法導致的了。
上面都排查完了,如果還是找不到問題原因,不妨換個思路,去仔細檢查下你參與構建的普通 java 文件,是否存在語法錯誤或者什么問題(譬如常量沒聲明等);如果有,解決完了再試試,別問我為什么,我也沒有深入研究javac這塊源碼,只是我遇到過,且也沒有異常堆棧信息,最終發現是合并解決沖突后代碼少了一個變量聲明,就是單純的越過了 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'. // 本來這里該先吐我注解處理器內部的日志,然后才繼續 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 構建命令已經添加了各種詳細參數供查看堆棧和詳細日志,但奇妙的事情就是他走到compileReleaseJavaWithJavac就直接出錯了,前后沒有任何錯誤提示(有的只是一坨 Gradle 自己的 task 調用鏈)。我特么大意了,我就同步了下代碼,編不過就編不過啊,你倒是提示下問題啊!啥也不提示直接干到 compile class 環節了,跳過了 Annotation Processor 流程,這就很惱火了。好在按照上面方式定位修復了,哈哈。
關于Annotation Processor 處理器問題如何深度定位問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。