You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
    }

}
问题原因及解决方案

错误原因

  1. 源文件路径定位错误:createSourceFile(className)仅传递了类名,未指定完整限定类名(包名+类名),导致Filer无法在被注解类的对应包路径下生成文件,反而尝试在默认包创建文件,引发路径冲突。
  2. 类名重复:生成的类名和原被注解类名完全一致,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:22:49