文章目录
一、报错信息
二、问题分析
三、解决方案
注解处理器 AbstractProcessor 中的 process 方法可能会调用多次 , 在生成代码时 , 一定要注意 , 检测到 注解节点 后再生成代码 ;
一、报错信息
Android 编译时技术 , 使用注解处理器生成代码 , 编译时报如下错误 :
( 该错误不会中断编译 )
javax.annotation.processing.FilerException: Attempt to recreate a file for type com.example.helloworld.HelloWorld at com.sun.tools.javac.processing.JavacFiler.checkNameAndExistence(JavacFiler.java:522) at com.sun.tools.javac.processing.JavacFiler.createSourceOrClassFile(JavacFiler.java:396) at com.sun.tools.javac.processing.JavacFiler.createSourceFile(JavacFiler.java:378) at com.squareup.javapoet.JavaFile.writeTo(JavaFile.java:169) at kim.hsl.router_compiler.RouterProcessor.process(RouterProcessor.java:91) at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62) at org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor.process(NonIncrementalProcessor.java:45) at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62) at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.access$401(TimeTrackingProcessor.java:37) at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$5.create(TimeTrackingProcessor.java:99) at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$5.create(TimeTrackingProcessor.java:96) at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.track(TimeTrackingProcessor.java:117) at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.process(TimeTrackingProcessor.java:96) at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794) at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705) at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91) at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035) at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176) at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170) at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:856) at com.sun.tools.javac.main.Main.compile(Main.java:523) at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129) at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138) at org.gradle.internal.compiler.java.IncrementalCompileTask.call(IncrementalCompileTask.java:74)
二、问题分析
根据上述报错信息提示 " Attempt to recreate a file " , 尝试重新创建一个文件 , 也就是说之前已经创建了一次文件 ;
注解处理器代码如下 :
package kim.hsl.router_compiler; import com.google.auto.service.AutoService; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; import java.io.IOException; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; // 自动注册注解处理器 @AutoService(Processor.class) // 支持的注解类型 @SupportedAnnotationTypes({"kim.hsl.router_annotation.Route"}) // 支持的 Java 版本 @SupportedSourceVersion(SourceVersion.RELEASE_8) public class RouterProcessor extends AbstractProcessor { /** * 注解处理器中使用 Messager 对象打印日志 */ private Messager mMessager; /** * 用于写出生成的 Java 代码 */ private Filer mFiler; /** * 该函数在初始化时调用 , 相当于构造函数 * @param processingEnvironment */ @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); // 获取打印日志接口 this.mMessager = processingEnvironment.getMessager(); mMessager.printMessage(Diagnostic.Kind.NOTE, "Messager Print Log"); this.mFiler = processingEnvironment.getFiler(); } /** * 该函数在注解处理器注册时自动执行, 是处理注解的核心函数 * * Set<? extends TypeElement> set 参数 : 该集合表示使用了相关注解的节点的集合 * * @param set * @param roundEnvironment * @return */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { for (TypeElement typeElement: set){ mMessager.printMessage(Diagnostic.Kind.NOTE, "SupportedAnnotationTypes : " + typeElement.getQualifiedName()); } // 生成 public static void main(String[] args) 函数 MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") .build(); // 指定 public final class HelloWorld 类 TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); // 正式在 "com.example.helloworld" 包名下创建 HelloWorld 类 JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); try { javaFile.writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); } return false; } }
在 process 方法中 , 使用 JavaPoet 生成 Java 代码 ;
上述 process 方法应该是调用 3 33 次 , 调用第一次时生成了 com.example.helloworld.HelloWorld 源码 , 但是后面又调用了 2 22 次 , 后面调用的 2 22 次直接报上述 " javax.annotation.processing.FilerException: Attempt to recreate a file for type com.example.helloworld.HelloWorld " 错误 ;
三、解决方案
AbstractProcessor 中的 process 方法调用了 3 33 次 , 但是只有 1 11 次 Set<? extends TypeElement> set 注解参数不为空 , 这里检测到注解后 , 再生成 Java 代码即可 ;
修改后的源代码如下 :
package kim.hsl.router_compiler; import com.google.auto.service.AutoService; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; import java.io.IOException; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; // 自动注册注解处理器 @AutoService(Processor.class) // 支持的注解类型 @SupportedAnnotationTypes({"kim.hsl.router_annotation.Route"}) // 支持的 Java 版本 @SupportedSourceVersion(SourceVersion.RELEASE_8) public class RouterProcessor extends AbstractProcessor { /** * 注解处理器中使用 Messager 对象打印日志 */ private Messager mMessager; /** * 用于写出生成的 Java 代码 */ private Filer mFiler; /** * 该函数在初始化时调用 , 相当于构造函数 * @param processingEnvironment */ @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); // 获取打印日志接口 this.mMessager = processingEnvironment.getMessager(); mMessager.printMessage(Diagnostic.Kind.NOTE, "Messager Print Log"); this.mFiler = processingEnvironment.getFiler(); } /** * 该函数在注解处理器注册时自动执行, 是处理注解的核心函数 * * Set<? extends TypeElement> set 参数 : 该集合表示使用了相关注解的节点的集合 * * @param set * @param roundEnvironment * @return */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { for (TypeElement typeElement: set){ mMessager.printMessage(Diagnostic.Kind.NOTE, "SupportedAnnotationTypes : " + typeElement.getQualifiedName()); // 生成 public static void main(String[] args) 函数 MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") .build(); // 指定 public final class HelloWorld 类 TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); // 正式在 "com.example.helloworld" 包名下创建 HelloWorld 类 JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); try { javaFile.writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); } } return false; } }
修改后 , 编译时不再报上述错误 ;