必须用 shadowJar 任务打包而非默认 jar,因后者不包含依赖导致运行时 NoClassDefFoundError;需配置 shadow 插件、指定 mainClass、设置 mergeServiceFiles() 和排除签名文件。

直接用 shadowJar 任务打包,不是 jar;否则打出来的包不含依赖,运行时必报 NoClassDefFoundError 或 ClassNotFoundException。
为什么不能只用默认的 jar 任务
Gradle 默认的 jar 任务只打包你项目 src 下编译出的 .class 文件,完全不碰 dependencies 里的任何第三方 jar。哪怕你在 build.gradle 里写了 implementation 'com.google.guava:guava:33.2.1-jre',生成的 jar 解压后也找不到 guava 的任何类。
运行 java -jar your-app.jar 时,JVM 只在该 jar 内部查类,不会去 ~/.m2 或 classpath 外找依赖——所以它一定失败。
- 验证方法:用
unzip -l build/libs/xxx.jar | grep -i guava,结果为空就说明没打进依赖 - 常见错误现象:
Exception in thread "main" java.lang.NoClassDefFoundError: com/google/common/collect/ImmutableList - 别被 IDE 运行按钮骗了——IDE 是把整个 classpath 加进去跑的,和
java -jar完全两回事
必须配置 shadow 插件并指定 mainClass
Shadow 不是 Gradle 内置插件,必须显式声明。Groovy DSL(build.gradle)中推荐写法:
立即学习“Java免费学习笔记(深入)”;
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '8.7.0'
}
然后在 shadowJar 块里指定主类,否则 java -jar 会报 no main manifest attribute:
shadowJar {
mainClass = 'com.example.Main'
archiveBaseName = 'myapp'
archiveVersion = '1.2.0'
archiveClassifier = ''
}
-
mainClass必须是完整类名(含包路径),不能写Main或src/main/java/com/example/Main.java -
archiveClassifier = ''是为了去掉默认的-all后缀;若保留,最终文件名是myapp-1.2.0-all.jar - 不要混用
jar { manifest { attributes 'Main-Class': ... } }—— shadow 有自己的 manifest 控制逻辑,重复设置可能冲突
资源冲突时得靠 mergeServiceFiles() 和 exclude
多个依赖都带 META-INF/services/ 下的同名文件(比如 java.nio.file.spi.FileSystemProvider),或都有 module-info.class,直接打包会失败或覆盖。
正确做法是在 shadowJar 块里加合并策略:
shadowJar {
mergeServiceFiles()
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
exclude 'META-INF/*.RSA'
}
-
mergeServiceFiles()会把所有META-INF/services/*文件内容追加合并,避免 SPI 机制失效 - 排除签名文件(
*.SF等)是必须的,否则打包会因重复签名报错 - 如果某个依赖的配置文件(如
logback.xml)你只想留自己模块的,用exclude 'logback.xml'即可
执行命令和验证方式
运行以下命令生成 fat jar:
./gradlew shadowJar
输出路径固定为:build/libs/<archivebasename>-<archiveversion>.jar</archiveversion></archivebasename>(无 classifier)或 ...-all.jar(有默认 classifier)。
- 检查是否真包含依赖:用
jar -tf build/libs/myapp-1.2.0.jar | grep -i guava,应看到大量com/google/common/...类路径 - 快速验证能否运行:
java -jar build/libs/myapp-1.2.0.jar,观察是否正常启动、无 ClassNotFoundException - 注意:不要用
./gradlew build代替shadowJar,它默认不触发 shadow 任务
最常被忽略的一点:Shadow 插件版本要匹配 Gradle 版本(例如 Gradle 8.6+ 推荐 Shadow 8.7.0),用旧版(如 7.1.2)在 Gradle 8.x 下可能静默跳过某些合并逻辑,导致运行时报奇怪的 ServiceConfigurationError。


















