文章目录
- 一、报错信息
- 二、问题分析
- 三、解决方案
注解处理器 AbstractProcessor 中的 process 方法可能会调用多次 , 在生成代码时 , 一定要注意 , 检测到 注解节点 后再生成代码 ;
一、报错信息
Android 编译时技术 , 使用注解处理器生成代码 , 编译时报如下错误 :
( 该错误不会中断编译 )
代码语言:javascript复制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 " , 尝试重新创建一个文件 , 也就是说之前已经创建了一次文件 ;
注解处理器代码如下 :
代码语言:javascript复制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 次 , 调用第一次时生成了 com.example.helloworld.HelloWorld 源码 , 但是后面又调用了 2 次 , 后面调用的 2 次直接报上述 " javax.annotation.processing.FilerException: Attempt to recreate a file for type com.example.helloworld.HelloWorld " 错误 ;
三、解决方案
AbstractProcessor 中的 process 方法调用了
3 次 , 但是只有 1 次 Set<? extends TypeElement> set
注解参数不为空 , 这里检测到注解后 , 再生成 Java 代码即可 ;
修改后的源代码如下 :
代码语言:javascript复制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;
}
}
修改后 , 编译时不再报上述错误 ;