如何在IntelliJ IDEA中使用SBT构建Uber JAR(Fat JAR)?该问题涉及IT技术。

100

我正在使用SBT(在IntelliJ IDEA中)构建一个简单的Scala项目。

我想知道构建“Uber JAR”文件(也称为Fat JAR、Super JAR)的最简单的方法是什么。

我目前正在使用SBT,但当我将我的JAR文件提交到Apache Spark时,会出现以下错误:

Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes

或者在编译时出现以下错误:

java.lang.RuntimeException: deduplicate: different file contents found in the following:
PATH\DEPENDENCY.jar:META-INF/DEPENDENCIES
PATH\DEPENDENCY.jar:META-INF/MANIFEST.MF

看起来是因为我的某些依赖项包括需要在最终的Uber JAR文件中删除的签名文件(META-INF),如此处所述

我尝试使用sbt-assembly插件来解决这个问题:

/project/assembly.sbt

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")

/project/plugins.sbt

logLevel := Level.Warn

/build.sbt

lazy val commonSettings = Seq(
  name := "Spark-Test"
  version := "1.0"
  scalaVersion := "2.11.4"
)

lazy val app = (project in file("app")).
  settings(commonSettings: _*).
  settings(
    libraryDependencies ++= Seq(
      "org.apache.spark" %% "spark-core" % "1.2.0",
      "org.apache.spark" %% "spark-streaming" % "1.2.0",
      "org.apache.spark" % "spark-streaming-twitter_2.10" % "1.2.0"
    )
  )

在 IntelliJ IDEA 中点击 "Build Artifact..." 后,我得到了一个 JAR 文件。但我最终遇到了相同的错误...

我对 SBT 不是很熟悉,也没有太多 IntelliJ IDE 的经验。

谢谢。


2
听起来你可能需要过滤掉 META-INF 文件 - 这篇博客或许可以帮到你:https://janschulte.wordpress.com/2014/03/20/removing-meta-inf-directory-from-sbt-assembly/ - Sean Vieira
3个回答

157

最后,我彻底停止使用IntelliJ IDEA,以避免在我的全局理解中产生噪音 :)

我开始阅读官方SBT教程

我按以下文件结构创建了我的项目:

my-project/project/assembly.sbt
my-project/src/main/scala/myPackage/MyMainObject.scala
my-project/build.sbt

我在assembly.sbt文件中添加了sbt-assembly 插件,使我能够构建一个大的JAR文件:

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")

我最基本的build.sbt看起来像:

lazy val root = (project in file(".")).
  settings(
    name := "my-project",
    version := "1.0",
    scalaVersion := "2.11.4",
    mainClass in Compile := Some("myPackage.MyMainObject")        
  )

val sparkVersion = "1.2.0"

libraryDependencies ++= Seq(
  "org.apache.spark" %% "spark-core" % sparkVersion % "provided",
  "org.apache.spark" %% "spark-streaming" % sparkVersion % "provided",
  "org.apache.spark" %% "spark-streaming-twitter" % sparkVersion
)

// META-INF discarding
mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
   {
    case PathList("META-INF", xs @ _*) => MergeStrategy.discard
    case x => MergeStrategy.first
   }
}

注意% "provided" 表示不将该依赖项包含在最终的fat JAR中(这些库已经包含在我的工作程序中)。

注意: META-INF 删除受此答案的启发(链接)

注意%%% 的含义。

现在,我可以在我的/my-project根文件夹中运行以下命令,使用SBT(如何安装它)构建我的fat JAR:

sbt assembly

我的胖JAR现在位于新生成的/target文件夹中:

/my-project/target/scala-2.11/my-project-assembly-1.0.jar

对于那些希望在IntelliJ IDE中嵌入SBT的人:如何从IntelliJ IDEA中运行sbt-assembly任务?


2
Java/Maven建议解决Databricks中的超级Jars排除Spark问题。 https://databricks.gitbooks.io/databricks-spark-knowledge-base/content/troubleshooting/missing_dependencies_in_jar_files.htm - JimLohse
5
@JimLohse 正确的链接是 https://databricks.gitbooks.io/databricks-spark-knowledge-base/content/troubleshooting/missing_dependencies_in_jar_files.html 。该链接提供了有关如何解决 Jar 文件中缺少依赖项的故障排除信息。 - Zeke Fast
1
废弃旧的META-INF的原因是什么? - qed
2
注意:提供的“%”表示不将依赖项包含在最终的fat JAR中,这对我很有帮助! - Jayasagar
1
非常惊讶这是 唯一 可用的插件 - 甚至不是官方的,而且还不能跨 sbt 版本工作。 - Abhinandan Dubey
显示剩余2条评论

43

在IntelliJ Idea中构建Uber JAR / Fat JAR的3个步骤:

Uber JAR / Fat JAR:包含所有外部库依赖关系的JAR文件。

  1. 在IntelliJ Idea中添加SBT Assembly插件

    Plugin sbt Path

    转到项目名称/项目目录/target/plugins.sbt文件并添加此行addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")

  2. 在build.sbt中添加合并、丢弃和不添加策略

    Build sbt Path

    转到项目名称/build.sbt文件并添加打包Uber JAR的策略

    合并策略:如果两个软件包之间存在有关库版本的冲突,则需要将哪一个打包成Uber JAR。
    丢弃策略:从库中删除一些文件,您不想将其打包成Uber JAR。
    不添加策略:不要将某些软件包添加到Uber JAR中。
    例如:spark-core已经存在于您的Spark Cluster中,因此我们不应该将其打包成Uber JAR

    合并策略和丢弃策略的基本代码:

    assemblyMergeStrategy in assembly := { case PathList("META-INF", xs @ _*) => MergeStrategy.discard case x => MergeStrategy.first }

您希望使用此命令MergeStrategy.discard丢弃META-INF文件,并对于其余的文件,如果存在冲突,则使用此命令MergeStrategy.first获取库文件的第一个出现

不要添加基本策略代码:

libraryDependencies += "org.apache.spark" %% "spark-core" % "1.4.1" %"provided"

如果我们不想将spark-core添加到我们的Uber JAR文件中,因为它已经在我们的集群中了,所以我们在其库依赖项末尾添加了% "provided"

  • 构建包含所有依赖项的Uber JAR

    sbtassembly

    在终端中键入sbt assembly以构建该包


  • Voila! Uber JAR已构建。 JAR将位于ProjectName/target/scala-XX

    JarBuilt


    17

    将以下一行代码添加到你的 project/plugins.sbt 文件中

    addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")
    

    在你的build.sbt文件中添加以下内容:

    mainClass in assembly := some("package.MainClass")
    assemblyJarName := "desired_jar_name_after_assembly.jar"
    
    val meta = """META.INF(.)*""".r
    assemblyMergeStrategy in assembly := {
      case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first
      case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first
      case n if n.startsWith("reference.conf") => MergeStrategy.concat
      case n if n.endsWith(".conf") => MergeStrategy.concat
      case meta(_) => MergeStrategy.discard
      case x => MergeStrategy.first
    }
    

    当创建fat jar时,使用Assembly合并策略来解决冲突。


    1
    您可以在控制台中运行“sbt assembly”来创建Fat Jar。 - ARMV
    2
    对于Scala版本2.11.8(SBT版本:0.13.12),请在project/assembly.sbt中添加addSbtPlugin(“com.eed3si9n”%“sbt-assembly”%“0.12.0”)。 - ARMV

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接