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

在自定义Java Gradle插件中使用Reflections生成注解类/方法报告

实现多模块Gradle项目中基于Reflections的注解报告插件

我来帮你搞定这个在多模块Gradle项目里用自定义Java插件结合Reflections生成注解报告的需求,咱们一步步来:

1. 梳理你已有的基础配置

先把你给出的配置清晰整理出来,确保基础结构没问题:

项目根目录 /build.gradle

apply plugin: com.my.plugins.MyPlugin

/buildSrc/build.gradle 依赖配置

这里引入Reflections和注解API的配置是正确的,不过如果你的Gradle版本在7.0+,推荐把compile换成implementation:

repositories { 
    mavenCentral() 
}
dependencies {
    implementation 'org.reflections:reflections:0.9.11'
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
}

插件类基础结构 /buildSrc/src/main/java/com/my/plugins/MyPlugin.java

@NonNullApi
public class MyPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        // 核心实现逻辑将在这里补充
    }
}

2. 核心实现:扫描多模块并生成报告

因为是多模块项目,每个模块都有独立的编译输出,所以我们要给每个子模块单独注册扫描任务,确保能覆盖所有模块的类。

步骤1:给子模块注册自定义任务

在MyPlugin的apply方法中,为每个子模块添加生成报告的任务,并绑定依赖关系:

@Override
public void apply(Project project) {
    // 遍历所有子模块,注册报告生成任务
    project.getSubprojects().forEach(subproject -> {
        TaskProvider<GenerateAnnotationReportTask> reportTask = subproject.getTasks().register(
                "generateAnnotationReport",
                GenerateAnnotationReportTask.class,
                task -> {
                    // 任务依赖子模块的Java编译任务,确保先编译再扫描
                    task.dependsOn(subproject.getTasks().named("compileJava"));
                    // 指定扫描目录:子模块的编译输出类目录
                    task.setClassesDir(subproject.getLayout().getBuildDirectory().dir("classes/java/main"));
                    // 指定报告输出路径
                    task.setOutputFile(subproject.getLayout().getBuildDirectory().file("reports/annotation-report.txt"));
                }
        );

        // 把报告任务挂到check任务的依赖中,执行check时自动生成报告
        subproject.getTasks().named("check").configure(checkTask -> checkTask.dependsOn(reportTask));
    });
}

步骤2:实现自定义任务类 GenerateAnnotationReportTask

这个类是核心,负责用Reflections扫描注解并生成报告:

import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Set;

public class GenerateAnnotationReportTask extends DefaultTask {

    private final DirectoryProperty classesDir = getProject().getObjects().directoryProperty();
    private final RegularFileProperty outputFile = getProject().getObjects().fileProperty();

    @InputDirectory
    public DirectoryProperty getClassesDir() {
        return classesDir;
    }

    @OutputFile
    public RegularFileProperty getOutputFile() {
        return outputFile;
    }

    @TaskAction
    public void generateReport() throws IOException {
        // 构建Reflections配置,指定要扫描的类目录
        ConfigurationBuilder config = new ConfigurationBuilder()
                .setUrls(ClasspathHelper.forDirectory(classesDir.get().getAsFile()))
                .setScanners(Scanners.TypesAnnotated, Scanners.MethodsAnnotated);

        Reflections reflections = new Reflections(config);

        // 替换成你实际要扫描的注解类
        Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(YourClassAnnotation.class);
        Set<java.lang.reflect.Method> annotatedMethods = reflections.getMethodsAnnotatedWith(YourMethodAnnotation.class);

        // 拼接报告内容
        StringBuilder reportContent = new StringBuilder();
        reportContent.append("=== Annotation Report for Module: ").append(getProject().getName()).append(" ===\n\n");
        
        reportContent.append("Classes with @YourClassAnnotation:\n");
        annotatedClasses.forEach(clazz -> reportContent.append("- ").append(clazz.getCanonicalName()).append("\n"));

        reportContent.append("\nMethods with @YourMethodAnnotation:\n");
        annotatedMethods.forEach(method -> reportContent.append("- ").append(method.getDeclaringClass().getCanonicalName())
                .append("#").append(method.getName()).append("\n"));

        // 写入报告文件
        FileWriter writer = new FileWriter(outputFile.get().getAsFile());
        writer.write(reportContent.toString());
        writer.close();

        getLogger().lifecycle("Generated annotation report at: {}", outputFile.get().getAsFile().getAbsolutePath());
    }
}

3. 关键注意事项

  • 替换注解类:一定要把代码里的YourClassAnnotation和YourMethodAnnotation替换成你实际需要扫描的注解类。
  • 扩展扫描范围:如果需要扫描带注解的字段、构造方法等,可以调整Scanners的类型,比如添加Scanners.FieldsAnnotated。
  • 依赖模块扫描:如果某个模块依赖其他模块的类,需要把依赖模块的编译输出目录也加入扫描路径,可以通过子模块的sourceSets获取依赖类路径。

4. 执行方式

在项目根目录执行以下命令,即可为所有子模块生成注解报告:

./gradlew generateAnnotationReport

或者执行check任务,会自动触发报告生成:

./gradlew check

内容的提问来源于stack exchange,提问作者G. Miller

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:34:12