
本文详解如何在gradle中通过正确配置shadow插件,彻底避免默认jar与shadowjar重复输出(如translator.jar和translator-1.0.jar共存),确保build/libs下仅生成唯一指定名称的可执行胖jar。
本文详解如何在gradle中通过正确配置shadow插件,彻底避免默认jar与shadowjar重复输出(如translator.jar和translator-1.0.jar共存),确保build/libs下仅生成唯一指定名称的可执行胖jar。
在使用Gradle + Shadow插件构建Java应用时,开发者常遇到一个典型困扰:明明只想要一个名为 Translator.jar 的可执行胖Jar,却在 build/libs/ 目录下同时看到 Translator.jar 和 Translator-1.0.jar ——后者正是Gradle默认 jar 任务(由Java插件自动注册)生成的标准Jar包。问题根源在于:你显式重写了 jar 任务为 ShadowJar 类型,但未禁用原始 jar 任务,也未解除其与 build 生命周期的绑定。
✅ 正确做法:禁用默认 jar,仅保留自定义 ShadowJar
Gradle的 build 任务默认依赖 jar 任务(即 classes → jar → assemble 链)。当你声明 task jar(type: ShadowJar),实质是覆盖了默认的 jar 任务定义,但该任务仍参与构建流程,且因版本号机制(archiveVersion 默认取 project.version),会生成带 -1.0 后缀的文件;而你另定义的 task build(type: ShadowJar) 则进一步造成任务冲突与冗余输出。
✅ 推荐配置(Gradle 7.0+,兼容8.x)
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '8.7.0''
// ⚠️ 关键:禁用默认 jar 任务(防止生成 Translator-1.0.jar)
id 'base' // 提供 tasks.withType() 基础支持(Gradle 8+ 可选)
}
group = 'translator'
version = '1.0'
sourceCompatibility = JavaVersion.VERSION_1_8
repositories { mavenCentral() }
dependencies {
implementation 'org.jetbrains:annotations:24.0.1'
implementation 'org.json:json:20230227'
}
// ? 彻底禁用默认 jar 任务(核心修复)
tasks.named('jar') {
enabled = false
}
// ✅ 定义唯一 fatJar 任务,并绑定到 build 生命周期(替代默认 jar)
tasks.register('fatJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
archiveBaseName = 'Translator'
archiveVersion = '' // 清空版本后缀 → 输出 Translator.jar 而非 Translator-1.0.jar
archiveExtension = 'jar'
from sourceSets.main.output
configurations = [project.configurations.runtimeClasspath] // 替代已废弃的 compile
mergeServiceFiles()
mergeContent()
// 排除签名与元数据冲突文件
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
// 指定主类(必需!否则 java -jar 报错)
manifest {
attributes 'Main-Class': 'translator.Main' // 替换为你的实际入口类
}
}
// ✅ 让 build 任务最终执行 fatJar(而非默认 jar)
tasks.named('build') {
dependsOn 'fatJar'
finalizedBy 'fatJar'
}
? 关键点说明:
tasks.named('jar').configure { enabled = false }是根治方案,直接移除默认jar任务的执行。- 使用
archiveVersion = ''(空字符串)而非archiveClassifier,可彻底消除版本号后缀;archiveBaseName控制基础名,archiveExtension显式设为jar。configurations.runtimeClasspath替代过时的configurations.compile,确保所有运行时依赖(含 transitive)被正确收集。dependsOn 'fatJar'+finalizedBy 'fatJar'确保执行./gradlew build时必然生成目标Jar。
⚠️ 注意事项与避坑指南
-
不要重写
jar或build任务本身:如原配置中task jar(...)和task build(...)并行定义,极易引发任务覆盖、依赖循环或Gradle静默忽略。 -
避免
overwrite: true(已弃用):Gradle 6.0+ 已移除此参数,应使用tasks.named().configure{}方式安全覆写。 -
验证是否生效:执行
./gradlew clean build && ls build/libs/,应仅见Translator.jar;再运行java -jar build/libs/Translator.jar确认可执行性。 -
Android项目特殊提示:若在AGP环境中,需额外禁用
assemble相关任务,并优先使用application插件的installDist或自定义ShadowJar任务,避免与Android打包生命周期冲突。
通过以上配置,你将获得真正“所见即所得”的构建结果:一个精确定制名称、无冗余版本号、全依赖内嵌、可直接 java -jar 运行的生产就绪Fat Jar——告别命名污染,回归构建本意。











