Gradle多模块下Java注解处理器生成类报错求助
问题描述
我正在使用Gradle多模块结构学习Java注解处理器,想要实现类似Lombok @Builder的注解。目前可以在build/generated/sources/annotationProcessor/java目录生成类,但尝试在被注解类的同包下生成类时,构建任务报错提示尝试替换文件。
项目结构
ROOT | |-sample # 包含主类 |-annotation # 供处理器读取的注解 |-processor # 用于生成代码的处理器
各模块配置代码
sample模块build.gradle
plugins { id("java") } group = "br.com.dio" version = "1.0-SNAPSHOT" repositories { mavenCentral() } dependencies { implementation(project(":annotation")) compileOnly(project(":annotation")) implementation(project(":processor")) annotationProcessor(project(":processor")) }
processor模块build.gradle
plugins { id("java") } group = "br.com.dio" version = "1.0-SNAPSHOT" repositories { mavenCentral() } val autoServiceVersion = "1.1.1" dependencies { compileOnly("com.google.auto.service:auto-service:$autoServiceVersion") annotationProcessor("com.google.auto.service:auto-service:$autoServiceVersion") implementation("com.squareup:javapoet:1.13.0") }
annotation模块build.gradle
plugins { id("java") } group = "br.com.dio" version = "1.0-SNAPSHOT" repositories { mavenCentral() } dependencies { }
settings.gradle.kts
rootProject.name = "annotation-processor" include("processor") include("sample") include("annotation")
Processor实现代码
public class BuilderProcessor extends AbstractProcessor { @Override public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) { for (TypeElement annotation : annotations) { for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) { Map<String, TypeMirror> fields =element.getEnclosedElements().stream() .filter(e -> e.getKind() == FIELD) .collect(Collectors.toMap(e -> e.getSimpleName().toString(), Element::asType)); final var packageName = processingEnv.getElementUtils().getPackageOf(element).toString(); final var className = element.getSimpleName().toString(); final var fullNameClass = packageName + "." + className; final var typeSpec = new BuilderCreate().create(packageName, className, fields); var javaFile = JavaFile.builder(packageName, typeSpec) .indent(" ") .build(); try( var out = new PrintWriter(processingEnv.getFiler() .createSourceFile(className) .openWriter() ) ){ out.println(javaFile); } catch (IOException e) { throw new RuntimeException(e); } } } return true; } }
问题原因及解决方案
错误原因
- 源文件路径定位错误:
createSourceFile(className)仅传递了类名,未指定完整限定类名(包名+类名),导致Filer无法在被注解类的对应包路径下生成文件,反而尝试在默认包创建文件,引发路径冲突。 - 类名重复:生成的类名和原被注解类名完全一致,Filer不允许覆盖已存在的源文件(原类在
src/main/java下),因此报错“尝试替换文件”。
修复步骤
1. 修改Processor代码,使用完整限定类名创建源文件
将createSourceFile(className)改为传递包名+生成类名,同时给生成的Builder类添加后缀(如Builder)避免和原类重名:
public class BuilderProcessor extends AbstractProcessor { @Override public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) { for (TypeElement annotation : annotations) { for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) { Map<String, TypeMirror> fields = element.getEnclosedElements().stream() .filter(e -> e.getKind() == FIELD) .collect(Collectors.toMap(e -> e.getSimpleName().toString(), Element::asType)); final var packageName = processingEnv.getElementUtils().getPackageOf(element).toString(); final var className = element.getSimpleName().toString(); // 定义生成的Builder类名,避免和原类重名 final var builderClassName = className + "Builder"; // 完整限定类名 final var fullBuilderClassName = packageName + "." + builderClassName; // 传递builderClassName给create方法,确保生成的类名正确 final var typeSpec = new BuilderCreate().create(packageName, builderClassName, fields); var javaFile = JavaFile.builder(packageName, typeSpec) .indent(" ") .build(); try( var out = new PrintWriter(processingEnv.getFiler() .createSourceFile(fullBuilderClassName) .openWriter() ) ){ // 使用JavaPoet内置方法写入,格式更可靠 javaFile.writeTo(out); } catch (IOException e) { throw new RuntimeException(e); } } } return true; } }
2. 优化sample模块依赖
移除不必要的implementation(project(":processor")),因为注解处理器不需要被主代码依赖,只需要annotationProcessor声明即可:
plugins { id("java") } group = "br.com.dio" version = "1.0-SNAPSHOT" repositories { mavenCentral() } dependencies { compileOnly(project(":annotation")) annotationProcessor(project(":processor")) }
关键说明
Filer.createSourceFile必须接收完整限定类名,这样才能在对应的包路径下生成文件,避免路径冲突。- 生成的辅助类(如Builder)必须和原类名区分开,禁止重名,否则Filer会因为尝试覆盖已有文件而报错。
- 使用
javaFile.writeTo(out)替代手动println,能保证生成代码的格式正确,避免额外的格式问题。
内容的提问来源于stack exchange,提问作者Jose Luiz Junior
相关产品推荐
相关产品推荐

