使用Java Poet生成方法级注解代码:处理器未执行无生成结果求助
问题排查:JavaPoet注解处理器未执行、代码未生成
用户场景与现状
想要实现方法级@Latency注解,通过JavaPoet在编译期生成耗时统计代码(替代AspectJ,方便调试查看具体逻辑),但当前注解处理器LatencyProcessor从未被调用,也看不到任何生成的代码。
注解定义
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Latency { }
注解处理器代码
package org.example; import com.google.auto.service.AutoService; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import java.io.IOException; import java.util.Set; @AutoService(Processor.class) public class LatencyProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes() { return Set.of(Latency.class.getName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { System.out.println("LatencyProcessor is running..."); for (Element element : roundEnv.getElementsAnnotatedWith(Latency.class)) { if (element.getKind().equals(ElementKind.METHOD)) { generateLatencyCode((ExecutableElement) element); } } return true; } private void generateLatencyCode(ExecutableElement methodElement) { String methodName = methodElement.getSimpleName().toString(); CodeBlock codeBlock = CodeBlock.builder() .beginControlFlow("try") .addStatement("long startTime = System.currentTimeMillis()") .addStatement("$N.$N()", "this", methodName) .addStatement("long endTime = System.currentTimeMillis()") .addStatement("System.out.println(\"Method $N execution time: \" + (endTime - startTime) + \" milliseconds\")", methodName) .nextControlFlow("catch (Exception e)") .addStatement("e.printStackTrace()") .endControlFlow() .build(); MethodSpec latencyMethod = MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class) .returns(TypeName.VOID) .addCode(codeBlock) .build(); TypeSpec latencyClass = TypeSpec.classBuilder("Latency_" + methodName) .addModifiers(Modifier.PUBLIC) .addSuperinterface(ClassName.get(methodElement.getEnclosingElement().asType())) .addMethod(latencyMethod) .build(); JavaFile javaFile = JavaFile.builder("generated", latencyClass) .build(); try { javaFile.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } } }
build.gradle配置
dependencies { implementation 'com.squareup:javapoet:1.13.0' implementation 'com.google.auto.service:auto-service:1.1.1' annotationProcessor 'com.google.auto.service:auto-service:1.1.1' } configurations { annotationProcessor }
问题原因与修复步骤
1. 注解处理器依赖配置错误
编译器无法发现你的LatencyProcessor,因为当前gradle配置未将处理器注册到编译流程中:
- 如果处理器和业务代码在同一模块,修改依赖配置:
dependencies { implementation 'com.squareup:javapoet:1.13.0' // auto-service用于生成处理器的META-INF注册文件,必须用annotationProcessor依赖 annotationProcessor 'com.google.auto.service:auto-service:1.1.1' // 确保当前模块的处理器类被纳入注解处理器路径 annotationProcessor files(sourceSets.main.output.classesDirs) } - 如果处理器是单独模块(推荐解耦),在业务模块的build.gradle中添加:
dependencies { implementation project(':processor-module') // 让业务模块能引用@Latency注解 annotationProcessor project(':processor-module') // 告诉编译器启用该模块的注解处理器 }
2. 注解RetentionPolicy不规范
当前注解用@Retention(RetentionPolicy.RUNTIME),虽然编译期也能识别,但注解处理器仅在编译期工作,更合理的配置是:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) // 仅保留到编译期,无需进入运行时 public @interface Latency { }
3. 处理器代码的逻辑问题
- 递归调用风险:生成的方法和原方法同名且标注
@Override,代码中this.process()会触发递归,应改为持有原对象引用并调用原方法,比如生成包装类:// 修改generateLatencyCode中的MethodSpec MethodSpec latencyMethod = MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class) .returns(TypeName.get(methodElement.getReturnType())) // 添加原对象作为参数或成员变量 .addParameter(ClassName.get(methodElement.getEnclosingElement().asType()), "delegate") .addCode(CodeBlock.builder() .beginControlFlow("try") .addStatement("long startTime = System.currentTimeMillis()") .addStatement("delegate.$N()", methodName) // 调用原对象方法,避免递归 .addStatement("long endTime = System.currentTimeMillis()") .addStatement("System.out.println(\"Method $N execution time: \" + (endTime - startTime) + \" milliseconds\")", methodName) .nextControlFlow("catch (Exception e)") .addStatement("e.printStackTrace()") .endControlFlow() .build()) .build(); - 类/接口适配问题:如果原方法在普通类而非接口中,
addSuperinterface会报错(类不能实现类),需改为继承原类(注意原类不能是final)。
4. 日志输出优化
System.out.println在注解处理器中可能无法正常打印到控制台,改用官方API输出日志:
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "LatencyProcessor is running...");
5. 生成代码位置检查
编译期生成的代码默认存放在build/generated/sources/annotationProcessor/main/java/目录下,而非项目源码目录,需到该路径下查看结果。
内容的提问来源于stack exchange,提问作者user1692342
相关产品推荐
相关产品推荐

