如何在Gradle的Jar任务中排除指定类?自定义插件场景
解决Gradle插件中用自定义类替换默认编译类的问题
问题分析
你遇到的核心问题:
- 直接修改sourceSet输出目录破坏构建链(依赖该sourceSet的后续任务会出现异常)
- Jar任务的exclude规则未生效,且触发重复类报错,根源在于:
- 默认Jar任务已自动包含
sourceSets.main.output,额外添加会导致输入重复 - 单个CopySpec的
DuplicatesStrategy设置不生效,需在Jar任务全局级别配置 - 排除逻辑中的路径转换错误,导致无法匹配到需要跳过的类文件
- 默认Jar任务已自动包含
解决方案
正确思路:控制Jar任务的输入来源,先加入自定义类目录,再加入默认sourceSet输出但排除被自定义类覆盖的文件,同时在Jar任务级别设置重复策略兜底,完全保留原有构建链。
Kotlin DSL 插件实现示例
class CustomPlugin : Plugin<Project> { override fun apply(project: Project) { val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) val mainSourceSet = sourceSets.named("main").get() val customClassesDir = project.layout.buildDirectory.dir("weaved-classes/java/main") project.tasks.named<Jar>("jar") { // 先添加自定义类目录,优先级高于默认类 from(customClassesDir) { include("**/*.class") } // 添加默认sourceSet输出,排除已被自定义类覆盖的文件 from(mainSourceSet.output) { val customClassPaths = customClassesDir.get().asFile.walkTopDown() .filter { it.isFile && it.extension == "class" } .map { // 转换为Jar包内的相对路径(统一用/分隔) customClassesDir.get().asFile.toURI().relativize(it.toURI()).path } .toSet() exclude { details -> customClassPaths.contains(details.relativePath.pathString) } } // 全局设置重复策略,兜底防止遗漏 duplicatesStrategy = DuplicatesStrategy.EXCLUDE } } }
Java 插件实现示例
public class CustomPlugin implements Plugin<Project> { @Override public void apply(Project project) { SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); SourceSet mainSourceSet = sourceSets.named("main").get(); Provider<Directory> customClassesDir = project.getLayout().getBuildDirectory().dir("weaved-classes/java/main"); project.getTasks().named("jar", Jar.class, jar -> { // 加入自定义类目录 jar.from(customClassesDir, spec -> { spec.include("**/*.class"); }); // 加入默认编译输出并排除重复类 jar.from(mainSourceSet.getOutput(), spec -> { // 预生成自定义类在Jar中的相对路径集合 Set<String> customClassPaths = customClassesDir.get().getAsFile().toPath() .walk(10) // 递归深度按需调整 .filter(Files::isRegularFile) .filter(path -> path.getFileName().toString().endsWith(".class")) .map(path -> { Path relative = customClassesDir.get().getAsFile().toPath().relativize(path); return relative.toString().replace(File.separatorChar, '/'); }) .collect(Collectors.toSet()); spec.exclude(details -> { String archivePath = details.getRelativePath().getPathString(); return customClassPaths.contains(archivePath); }); }); // Jar任务级别设置重复策略,覆盖全局 jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); }); } }
关键要点说明
- 输入顺序:先添加自定义类目录,配合
DuplicatesStrategy.EXCLUDE确保自定义类覆盖默认类 - 路径匹配:统一使用
/作为路径分隔符,保证和Jar包内的相对路径完全匹配 - 重复策略:必须在Jar任务全局设置
duplicatesStrategy,单个CopySpec的设置无法作用于整个任务 - 构建链保护:不修改sourceSet的默认输出目录,仅在Jar打包阶段控制内容,完全兼容原有构建流程
为什么之前的代码失败?
- Kotlin代码中,
exclude("*")仅对当前from块生效,无法覆盖Jar任务默认包含的sourceSet输出 - Java代码中,路径转换逻辑错误(用本地文件路径而非Jar内相对路径),导致排除规则无法匹配目标文件
DuplicatesStrategy设置在单个CopySpec中,未作用于整个Jar任务,因此仍会触发重复类报错
内容的提问来源于stack exchange,提问作者a a
相关产品推荐
相关产品推荐

