1. 概述

在 Java 和 Scala 项目中,我们经常需要将整个应用程序打包成一个独立的 JAR 文件,方便分发和部署。这种打包方式可以包含源码、资源文件等。

本文将介绍什么是 Fat JAR,如何通过 SBT 构建它,以及如何自定义构建过程。

2. 什么是 Fat JAR?

当我们执行 sbt package 命令时,SBT 默认会生成一个 thin JAR —— 它只包含我们自己写的代码和资源文件,不包括依赖项。

Fat JAR(也叫 Uber JAR)则包含了项目所有的依赖库,形成一个完全自包含的可执行文件。

✅ 优点:部署简单,无需担心目标环境是否已安装依赖
❌ 缺点:体积大,可能会引入冲突或冗余依赖

3. 使用 sbt-assembly 插件构建 Fat JAR

sbt-assembly 是 SBT 中最常用的插件之一,专门用于构建 Fat JAR。它灵感来源于 Maven 的 assembly 插件。

3.1 安装插件

首先,在项目根目录下的 project 文件夹中创建 plugins.sbt 文件:

project-root
 |----build.sbt
 |----src
 |----project
      |----plugins.sbt
      |----build.properties

然后在 plugins.sbt 中添加如下内容:

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

3.2 执行打包命令

进入项目根目录后运行:

> sbt assembly

SBT 会在 target/scala-<version>/ 目录下生成一个 Fat JAR 文件,例如:

target/scala-2.12/scala-sbt-assembly-1.0.jar

3.3 自定义 JAR 名称

可以通过在 build.sbt 中设置 assemblyJarName 来修改输出文件名:

assemblyJarName in assembly := "baeldung-scala-sbt-assembly-fatjar-1.0.jar"

3.4 排除特定依赖

有些依赖我们并不希望被打包进 Fat JAR(比如运行环境已经提供的库),这时可以在 build.sbt 中将其标记为 "provided"

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

⚠️ 注意:“provided” 表示该依赖由运行环境提供,不需要打包进 JAR。

3.5 处理重复文件冲突

使用 sbt-assembly 时经常会遇到 deduplicate 错误,尤其是 META-INF 目录下的文件冲突。

可以通过配置 merge strategy 来解决:

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

上面这段配置的意思是:

  • 遇到 META-INF 下的文件直接丢弃
  • 其他冲突文件保留第一个出现的版本

💡 更多 merge strategy 可参考官方文档:Merge Strategy

4. 小结

Fat JAR 确实让部署变得简单粗暴,尤其适合单机脚本、CLI 工具等场景。但也要注意:

⚠️ 不要一股脑把所有依赖都打进去,特别是那些依赖树庞大的库(如 Spark、Akka 等),容易造成包膨胀甚至类冲突。

建议:

  • 对于运行环境已有的依赖,尽量标记为 provided
  • 合理控制依赖数量,避免“依赖地狱”

📖 示例代码可以在 GitHub 查看:Baeldung/scala-tutorials


原始标题:Creating a Fat JAR Using SBT