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