使用Gradle合并拆分包的JAR文件(Java 9模块迁移场景)
嘿,作为刚接触Gradle和Java 9模块的开发者,遇到拆分包问题确实挺棘手的——尤其是把依赖都移到模块路径之后,这类冲突会直接阻碍模块的正常运行。针对你的Web应用(WAR包+Gradle构建),这里有几个实用的解决方案:
方案1:用Shadow Gradle插件合并JAR(最推荐)
Shadow是Gradle生态里专门处理胖JAR构建的插件,对拆分包的处理非常友好,还能帮你自动处理模块信息的兼容问题。
- 先在
build.gradle中引入插件:
plugins { id 'java-library' id 'eclipse-wtp' id 'war' id 'com.github.johnrengelman.shadow' version '7.1.2' // 可以替换为最新稳定版 }
- 配置Shadow插件处理拆分包:
shadowJar { // 合并依赖中的服务配置文件(比如log4j的SPI文件) mergeServiceFiles() // 核心:如果多个JAR共享同一个包,要么合并,要么重定位冲突包 // 示例:把冲突的com.example.utils包重命名到自己模块的shaded目录下 relocate 'com.example.utils', 'pk.training.basit.kanban.shaded.com.example.utils' // 给合并后的JAR添加Java模块信息 manifest { attributes 'Module-Class': project.ext.moduleName } }
- 执行任务生成合并后的JAR:
./gradlew shadowJar
生成的胖JAR会在build/libs目录下,命名格式一般是[项目名]-all.jar,直接把它放到模块路径就能用了。
方案2:手动扩展Jar任务合并依赖(无第三方插件)
如果你不想引入额外插件,可以直接修改Gradle的默认jar任务来手动合并依赖:
jar { // 先包含自己项目编译后的class文件 from sourceSets.main.output // 遍历所有运行时依赖的JAR,把里面的class文件合并进来 configurations.runtimeClasspath.each { file -> if (file.name.endsWith('.jar')) { from zipTree(file) { // 排除META-INF下的签名文件,避免合并后出现签名冲突 exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA' } } } // 处理重复class文件的策略:INCLUDE表示保留所有,WARN会打印警告,EXCLUDE会丢弃重复项 duplicatesStrategy = DuplicatesStrategy.INCLUDE }
执行./gradlew jar就能得到合并后的JAR,但这种方式需要自己处理依赖传递、模块信息兼容等细节,适合简单场景。
方案3:针对WAR包的特殊处理
因为你的项目是WAR类型,也可以直接在WAR任务中处理冲突依赖:
war { // 方式1:把冲突的JAR合并成一个新JAR,放到WEB-INF/lib下 into('WEB-INF/lib') { // 筛选出需要合并的冲突依赖(替换成你实际的依赖组/名称) from configurations.runtimeClasspath.filter { it.name.contains('log4j') || it.name.contains('commons-logging') } eachFile { file -> // 临时创建一个合并后的JAR文件 def mergedJar = fileTree(dir: temporaryDir).createTempFile('merged-deps', '.jar') zipTree(file.source).copyTo(zipTree(mergedJar)) file.source = mergedJar } } // 方式2:直接把依赖的class文件打包到WEB-INF/classes,避免JAR层面的冲突 from configurations.runtimeClasspath.collect { zipTree(it) } { exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA' } }
额外小贴士
- 优先检查依赖版本:很多拆分包问题是因为依赖了同一库的不同版本,尝试升级到最新稳定版,可能冲突就自动消失了。
- 合并后验证模块信息:如果用Java模块系统,记得检查合并后的JAR里的
module-info.class是否正确,Shadow插件可以通过transform(ModuleInfo.class)自动处理模块合并。 - 测试要全面:合并后一定要跑一遍单元测试和集成测试,确保没有出现类找不到、模块无法解析的问题。
内容的提问来源于stack exchange,提问作者Basit
相关产品推荐
相关产品推荐

