在Zeppelin加载外部FAT-JAR时遭遇java.lang.NoSuchMethodError求助
我来帮你拆解下这个java.lang.NoSuchMethodError的问题——虽然你是在EMR内部构建的FAT JAR,版本冲突概率低,但结合你用了公共子模块+sbt assembly的场景,大概率是打包合并策略或者子模块依赖传递的锅,咱们一步步排查:
1. 优先检查sbt assembly的合并策略
这是FAT JAR打包最容易踩的坑:当assembly把主模块、公共子模块以及所有依赖的类打包到一个JAR时,同名类/资源文件的合并规则如果不合理,就会覆盖掉你需要的类版本,导致方法缺失。
- 打开你的
build.sbt,查看assemblyMergeStrategy配置。默认策略对class文件通常是MergeStrategy.first(保留第一个找到的版本),如果公共子模块和主模块依赖了同一个库但版本不同,就可能保留了旧版本的类,而你调用的方法只在新版本里存在。 - 针对公共子模块,你可以针对性调整合并策略,比如确保子模块的类不会被覆盖:
assemblyMergeStrategy in assembly := { case PathList("com", "yourcompany", "common", xs @ _*) => MergeStrategy.first // 保留公共子模块的类 case PathList("META-INF", xs @ _*) => MergeStrategy.discard // 丢弃冲突的元数据文件 case x => MergeStrategy.first }
2. 排查公共子模块的依赖传递冲突
公共子模块可能引入了和主项目版本不一致的依赖,哪怕你在主项目里指定了版本,子模块的传递依赖还是可能“偷偷”带进来。
- 用
sbt dependencyTree命令生成完整的依赖树,搜索带[warn]标记的冲突项,看看是不是有某个库的旧版本被包含进来。比如如果公共子模块依赖了旧版本的Guava,而主项目用了新版本,就可能出现方法不兼容。 - 解决办法是在主项目的
build.sbt里用dependencyOverrides强制统一所有模块的依赖版本:
dependencyOverrides ++= Seq( "org.apache.spark" %% "spark-core" % "2.2.1", "org.apache.spark" %% "spark-sql" % "2.2.1", "org.scala-lang" % "scala-library" % "2.11.11" )
这样能确保公共子模块和主项目用完全一致的依赖版本。
3. 验证FAT JAR的内容完整性
有时候assembly打包可能漏掉了公共子模块的类,或者打包的类版本不对。
- 用
jar tf your-fat-jar-name.jar命令列出JAR里的所有文件,检查公共子模块的类是否存在。比如你的公共子模块包名是com.yourcompany.common,就搜索这个路径下的类是否都在。 - 用
javap -cp your-fat-jar-name.jar com.yourcompany.common.YourClass查看类的方法签名,确认你调用的那个方法是否存在。 - 另外,别忘了把Spark相关依赖设为
provided范围!如果你的build.sbt里没有把Spark依赖标记为Provided,assembly会把Spark的库打包进FAT JAR,和EMR自带的Spark 2.2.1冲突。正确配置如下:
libraryDependencies += "org.apache.spark" %% "spark-core" % "2.2.1" % Provided libraryDependencies += "org.apache.spark" %% "spark-sql" % "2.2.1" % Provided
4. 确认子模块的构建配置一致性
公共子模块的Scala版本、Spark版本必须和主项目完全匹配,哪怕是小版本差异也可能导致字节码不兼容。
- 检查公共子模块的
build.sbt,确保scalaVersion是2.11.11,Spark依赖版本是2.2.1,和主项目、EMR环境完全一致。
先从这几个方向排查,尤其是合并策略和依赖树,这两个是这类问题的高发区。
内容的提问来源于stack exchange,提问作者y2k-shubham
相关产品推荐
相关产品推荐

