当前位置: 首页 > 科技观察

如何深入定位AnnotationProcessor处理器问题?

时间:2023-03-14 20:59:34 科技观察

什么是AnnotationProcessor构造问题写过自定义annotationprocessor的老司机乍一看觉得这个问题挺简单的,是的,因为网上基本都是教你怎么登录的,但是你有没有想过但是如果你不会连log都打印出来了,怎么定位?比如下面的代码://确认META-INF/services/javax.annotation.processing.Processorisok//确认构建脚本ok,同时确认注解Bridge已经被使用并参与构建@AutoService(Processor.class)publicclassTestAnnotationProcessorextendsAbstractProcessor{publicTestAnnotationProcessor(){System.out.println("TestAnnotationProcessorconstrator");}@Overridepublicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnvironment){printsuper.init(Enronment)nprocessingEnvironment){printsuper.init(Enronment)nprocessing;TestAnnotationProcessorinit");}@OverridepublicSetgetSupportedAnnotationTypes(){System.out.println("TestAnnotationProcessorgetSupportedAnnotationTypes");Setsupported=newHashSet();supported.add(Bridge.class.getCanonicalName());返回支持;}@Overridepublicbooleanprocess(Setset,RoundEnvironmentroundEnvironment){System.out.println("TestAnnotationProcessorprocess");returntrue;}}运行build后compileReleaseJavaWithJavac没有先吐出我AnnotationProcessor的任何一行log,直接报错找不到我的annotationprocessorproductclassreference(也就是直接编译类链接已经进行了)你是不是一头雾水?反正我是糊涂了!打印日志不行,哈哈,确认环境没问题,搞什么鬼,直接绕过AnnotationProcessor编译。这时候你需要做一点深入的定位和分析(javac源码的大佬们请自行传递),前提是你需要熟悉AnnotationProcessor的基本原理,然后我们将使用一些额外的javac详细日志进行示例分析。AnnotationProcessor机制注解和注解处理器是JDK5引入的一种机制,主要用于为类、方法、字段和参数等Java结构提供附加信息。例如,常见的@Override是一个只对Java编译器有效的注解。Java允许我们自定义注解。自定义注解处理器就是用来处理这些自定义注解的(废话)。注解处理器触发的时机是由javac处理的,所以整个javac过程的简要步骤如下:javac主要步骤可以看到,javac编译概览图主要分为以下几个步骤:抽象语法树。调用已注册的注解处理器。如果注解处理器在处理过程中产生了新的源文件,则编译器重复步骤1和2,当注解处理器不再产生新的源文件时,进入最后一轮。进入真正的编译字节码链接生成字节码。以上就是注解处理器的核心机制。随着对这一核心机制的了解,我们将继续探索。构建工具下AnnotationProcessor的本质在我们日常的开发中(无论是Java后端还是Android移动端),无论是什么构建工具(Gradle或者Maven等),我们都会或多或少的使用到JDK提供的注解处理器能力。)哪些处理器是通过javac-processorpath命令参数显式指定的,或者META-INF/services/javax.annotation.processing.Processor被显式声明为由javac发现和调用(参见google的AutoService框架)。一般情况下,我们开发中AnnotationProcessor技术的使用和构建就是以上几步的解决,按照网上抄的大部分都能正常工作。rule语句放好,其他javacs可以自己完美调用。增强javac进程打印暴露问题解决AnnotationProcessor中自加日志不打印的问题,需要获取一些额外的信息来辅助定位。由于直接使用命令行javac是最原始的操作,我们一般使用Gradle进行构建,而Gradle的本质就是调用javac,所以我们以Gradle为例分析一下如何定位AnnotationProcessor问题。下面简单粗暴一点就是直接给根目录的build.gradle中的所有模块添加参数://参数可选,重点是-verbose-XprintRounds-XprintProcessorInfoallprojects{gradle.projectsEvaluated{tasks.withType(JavaCompile){options.compilerArgs<<"-Xlint"<<"-verbose"<<"-XprintRounds"<<"-XprintProcessorInfo"<<"-Xmaxerrs"<<"100000"}}}你也可以只使用注释处理器自己的modules添加,同上,只是在JavaCompile中添加参数即可。这里的参数其实就是我们平时命令行javac的参数。不明白的可以去命令行执行javac-help观察一下意思,如下(JDK8,不同版本的JDK略有不同):yan@yanDeMackbookPro:~$javac-helpUsage:javac其中,可能的选项包括:-g生成所有调试信息...-关于编译器正在做什么的详细输出消息...-processor[,,。..]要运行的注释处理器的名称;绕过默认的搜索过程-processorpathspecifiedwheretolookforannotationprocessors...as脚本中其他几个javac-help中没有的参数,可以参考官方文档https://docs.oracle.com/en/java/javase/11/tools/javac.html,详细解释了参数的含义。添加以上参数后,一定要将你的buildlog追加到一个磁盘文件中,因为log会变得非常大,定位问题也会变得容易。通过分析和定位构建日志中的问题来执行您的构建任务。完成后的分析定位主要分为以下几个步骤。每一步都是一个场景定位,可以一步步进行定位分析。1.在你的日志中搜索你的Processor类名,比如TestAnnotationProcessor.class,你看到的日志会是这样的。//如果你的注解处理器是项目中的源码日志[loadingRegularFileObject[/home/user/yan/test/target/classes/cn/yan/test/TestAnnotationProcessor.class]]//如果你的注解处理在项目中,设备依赖jar形式的log[loadingZipFileIndexFileObject[.../test.jar(cn/yan/test/TestAnnotationProcessor.class)]]分析:如果你的log中找不到以上信息,说明你的注解处理器没有添加到javac的类路径中。一般的问题是你的META-INF/services/javax.annotation.processing.Processor声明有问题,javac找不到你的注解处理器。有的同学可能会用google的AutoService生成META-INF/services/javax.annotation.processing.Processor。这种情况就得自己检查是否OK了(比如Android中的AGP以前在中间过渡版本修改过一段时间的classpath,需要手动把compile改成annotationProcessor)。2.在您的日志中搜索关键字Round。建议直接搜索Round1:这种格式比较简单,看到的log会是这样的。Round1:inputfiles:{cn.yan.test.Application,......,cn.yan.test.UseMarkedAnnotation}annotations:[java.lang.Override,cn.yan.annotation.Bridge]lastround:false上面的日志inputfiles:部分是你扫描的源代码,annotations:部分是你代码中使用的注解,如果你的注解处理器声明处理这样的注解(比如@cn.yan.annotation.Bridge),log是正常的,如上.分析:如果在log中没有找到上面的Round,说明javac没有触发任何注解处理器(不管是自己写的还是依赖第三方框架)。最大的疑点是检查你是否禁用了javacannotationprocessing装置,即确认javac执行时没有-proc:none参数。如果你的日志中有Round,但是inputfiles:和annotations:没有你的注解类和使用类,这意味着你没有在你的代码中使用你的注解处理器需要处理的注解。3、在你的日志中搜索Loadedcn.yan.test.TestAnnotationProcessor关键字,你看到的日志会是这样的。[Loadedcn.yan.test.TestAnnotationProcessorfromfile:/home/user/yan/test/target/classes/cn/yan/test/TestAnnotationProcessor.class]分析:如果看不到上面的log,说明你的annotationprocessor的class本身没有加载成功,不知道怎么分析为什么没有加载成功,但至少说明没有加载成功,可能需要仔细检查哪里不规范或者不规范。以上检查完毕,如果还是找不到问题的原因,不妨换个思路,仔细检查自己参与构建的普通java文件是否存在语法错误或其他问题(如未声明的常量等);如果有,先解决再试试,不要问我为什么,我没有深入研究javac的源码,但是遇到了,也没有异常堆栈信息,最后发现代码少了合并和解决冲突后的变量声明,即简单地跨过AnnotationProcessor过程直接进入compiletoclass过程)。这个技能有什么用?说实话,我也是老司机了。6年前玩过AnnotationProcessor,后来项目升级建设和Java8、androidX支持后,合并了代码,然后项目中的annotationprocessor和dataBinding都不行了。更可气的是,不行的时候真是小气。没有错误堆栈。如下:FAILURE:Buildfailedwithanexception.*Whatwentwrong:Executionfailedfortask':test:compileReleaseJavaWithJavac'.//我应该先吐出我注解处理器的内部日志,然后继续编译javac,但实际上什么都没有>Compilationfailed;seethecompilererroroutputfordetails.*Exceptionis:org.gradle.api.tasks.TaskExecutionException:Executionfailedfortask':moffice:compileReleaseJavaWithJavac'.atorg.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:200)...引起:org.gradle.api.internal.tasks.compile.CompilationFailedException:编译失败;seethecompilererroroutputfordetails.atorg.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:57)Gradle构建命令添加了各种详细的参数,用于查看堆栈和详细日志,但是一些美妙的东西就是,当他去compileReleaseJavaWithJavac,他直接报错,前后没有任何错误提示(有的只是一堆Gradle自己的taskcallchain)我大意了,我把代码同步了,补不上就补不上,但是要提示问题!什么都不提示直接进入compileclass链接,跳过AnnotationProcessor过程,很烦人。还好按照上面的方法修好了,哈哈。本文转载自微信公众号“码农每日一问”,可通过以下二维码关注。转载本文请联系码农日报问题公众号。