如何用Gradle创建仅含自研模块的可发布Shaded Jar?
问题背景
我们需要从多模块Spring Boot项目的数据层(very-neat-bigdata模块)发布一个可供客户(大概率是Spring项目)使用的库,核心需求如下:
- 自研模块(
very-neat-bigdata、very-neat-util、very-neat-other)需做Shade处理,打包到发布的jar中; - 第三方依赖(如Spring、HBase、Guava等)不做Shade,但要出现在发布的pom.xml中,且不强制客户使用我们指定的版本;
- 无需客户逐个排除依赖,依赖版本由客户项目自行控制。
现有项目结构
very-neat-project very-neat-web -> spring-web 1.5.4 very-neat-bigdata <- 目标模块 -> spring-context 1.5.4 -> hbase -> very-neat-util -> guava 26 -> very-neat-other -> common.util 1.5.6 -> ...其他大量依赖...
客户引入后的期望结构
customer-project customer-web -> spring-web 1.6.8 -> spring-context 1.6.8 -> very-neat-bigdata-shaded <- 我们提供的库 -> guava 28 -> hbase -> common.util 1.5.4 -> ..其他大量依赖..
Gradle实现方案
通过Gradle的Shadow插件可以满足需求,具体配置步骤如下:
1. 引入Shadow插件
在very-neat-bigdata模块的build.gradle中添加插件依赖:
plugins { id 'java' id 'maven-publish' id 'com.github.johnrengelman.shadow' version '7.1.2' }
2. 自定义依赖配置
创建专门用于Shade自研模块的配置,隔离自研与第三方依赖:
configurations { // 继承implementation配置,用于标记需要Shade的自研依赖 shadowImplementation.extendsFrom implementation }
3. 配置依赖关系
将自研模块放入shadowImplementation(会被打包到jar),第三方依赖放入implementation(作为传递依赖出现在pom):
dependencies { // 自研模块:将被Shade到最终发布的jar中 shadowImplementation project(':very-neat-util') shadowImplementation project(':very-neat-other') // 第三方依赖:不Shade,作为传递依赖暴露给客户,版本由客户控制 implementation 'org.springframework:spring-context' implementation 'org.apache.hbase:hbase-client' // 其他第三方依赖... }
4. 配置ShadowJar任务
指定要打包的自研代码包,排除第三方类,同时设置发布jar的名称:
shadowJar { // 包含自研模块的包路径(根据实际包名调整) include 'com/veryneat/bigdata/**' include 'com/veryneat/util/**' include 'com/veryneat/other/**' // 排除第三方依赖的类,避免打包进Shaded jar exclude 'com/google/**' exclude 'org/apache/hbase/**' exclude 'org/springframework/**' // 其他第三方包路径... // 设置最终jar名称为 very-neat-bigdata-shaded archiveBaseName.set('very-neat-bigdata-shaded') archiveClassifier.set('') // 移除默认的"shadow"后缀 }
5. 配置发布任务
确保发布的构件是Shaded jar,同时生成的pom.xml正确包含第三方依赖:
publishing { publications { maven(MavenPublication) { from components.java // 指定使用ShadowJar生成的jar作为发布构件 artifact shadowJar { classifier = '' } // 优化pom依赖:让第三方依赖版本可被客户覆盖 pom { dependencies { configurations.implementation.allDependencies.each { dep -> if (dep.group && dep.name) { dependency { groupId dep.group artifactId dep.name // 使用版本范围或留空(Maven需版本,建议用范围如"[1.5.4,)") version dep.version ?: '[1.0,)' scope 'compile' } } } } } } } // 配置发布仓库(如公司私服) repositories { maven { url = uri('https://your-company-maven-repo.com') credentials { username = project.findProperty('repoUser') ?: System.getenv('REPO_USER') password = project.findProperty('repoPass') ?: System.getenv('REPO_PASS') } } } }
最佳实践建议
- 依赖管理优先:如果是Spring生态模块,建议继承Spring Boot的
dependencyManagement,不引入starter,让客户的Spring Boot版本自动覆盖依赖版本,减少冲突。 - 严格隔离自研与第三方依赖:通过Shadow插件的自定义配置明确区分两类依赖,避免误打包第三方类导致类加载冲突。
- 不Shade核心框架:Spring、Guava这类核心库绝对不要Shade,依赖调解机制会自动处理版本优先级,强制Shade反而容易引发兼容性问题。
- 提供清晰的集成文档:告知客户哪些第三方依赖会自动引入,以及如何通过
dependencyManagement覆盖版本,降低集成成本。 - 多版本兼容性测试:在不同版本的Spring、第三方依赖环境下测试Shaded模块,确保兼容性。
内容的提问来源于stack exchange,提问作者Daniel Hári
相关产品推荐
相关产品推荐

