使用sbt-assembly构建Spark Uber Jar时,为何无法Shade json4s?
我来帮你拆解一下可能的原因,结合sbt-assembly 0.14.6的特性和Spark应用的依赖环境,主要是这几个配置细节容易踩坑:
1. 遗漏了json4s的核心依赖模块
json4s-native并不是独立的,它依赖json4s-core模块。如果你的shade规则只针对json4s-native,那json4s-core里的类还是会保留原包名,和Spark自带的json4s版本(比如Spark 2.x通常带的是3.2.x版本)冲突,导致shade没起到完全作用。
你需要把core模块也加入shade规则里,因为inLibrary只会处理你指定的模块,不会自动处理传递依赖。
2. Scala版本匹配与依赖声明的准确性
你第一次用%%(自动匹配Scala版本),第二次手动指定_2.11,这里要确保:
- 你的Spark版本对应的Scala版本确实是2.11(比如Spark 2.4.x适配Scala 2.11,Spark 3.x适配Scala 2.12+)
- 依赖声明的模块ID和shade规则里的完全一致,比如不能把
org.json4s:json4s-native_2.11写错成org.json4s:json4s-native(不带Scala版本后缀)
3. Spark自带依赖的优先级干扰
Spark本身已经包含了json4s的特定版本,sbt-assembly默认会处理依赖冲突,但如果你的shade规则没有明确覆盖Spark引入的json4s类,就会出现“shade了自己的依赖,但Spark自带的还是原包名”的情况。这时候需要确保shade规则同时作用于你的项目代码和依赖库(也就是.inProject和.inLibrary都要添加)。
4. 错误的shade规则写法
可能你没正确配置assemblyShadeRules的结构。比如,正确的配置应该同时指定要重命名的包路径、目标依赖模块,以及作用于项目本身。
可参考的正确配置示例
// 插件配置(你已添加) addSbtPlugin("com.eed3si9n" %% "sbt-assembly" % "0.14.6") // 依赖声明(用%%自动匹配Scala版本更稳妥) libraryDependencies ++= Seq( "org.json4s" %% "json4s-native" % "3.5.3", "com.typesafe" % "config" % "1.3.3" ) // 核心Shade规则配置 assemblyShadeRules in assembly := Seq( // 处理json4s所有相关模块 ShadeRule.rename("org.json4s.**" -> "shaded.org.json4s.@1") .inLibrary("org.json4s" %% "json4s-native" % "3.5.3") .inLibrary("org.json4s" %% "json4s-core" % "3.5.3") .inProject, // 确保项目代码里引用的json4s类也被重命名 // 处理typesafe config ShadeRule.rename("com.typesafe.config.**" -> "shaded.com.typesafe.config.@1") .inLibrary("com.typesafe" % "config" % "1.3.3") .inProject ) // 可选:排除Spark自带的json4s依赖,彻底避免冲突 assemblyExcludedJars in assembly := { val cp = (fullClasspath in assembly).value cp.filter { jar => jar.data.getName.startsWith("json4s-") } }
5. 依赖范围的问题
如果你的json4s或config依赖被标记为provided,sbt-assembly会跳过这些依赖的处理,自然也不会执行shade。确保你的依赖是默认的compile范围(不需要额外指定,默认就是)。
最后,你可以用sbt dependencyTree命令查看完整的依赖树,确认json4s的所有模块都被正确引入,并且没有和Spark的版本冲突。
内容的提问来源于stack exchange,提问作者Don Branson

