什么是fat JAR?

191

我听人们说过他们创建了一个fat JAR并进行部署,他们实际上是指什么?


当您想将产品作为jar文件发布时,通常希望将所有依赖的jar文件中的类放入一个单独的大jar文件中。 - nishu
@Nadeem_MK http://meta.stackexchange.com/questions/5280/embrace-the-non-googlers http://meta.stackexchange.com/questions/8724/how-should-we-deal-with-google-questions - eis
2
原始答案指向此链接:http://www.javacodegeeks.com/2012/11/packing-your-java-application-as-one-or-fat-jar.html - George Stocker
3
我不确定“fat jar”是否是Java中静态链接的等价物。 - Sridhar Sarnobat
@SridharSarnobat 不完全是这样。 "thin jar" 相当于静态链接的 C 或 C++ 应用程序。 "fat jar" 包括运行时,但对于静态链接的应用程序,当然包括底层操作系统和系统库。 - Rob Wells
6个回答

147

不同的名称只是将Java应用程序进行打包的方式。

精简版 - 仅包含您在代码编辑器中直接键入的内容,没有其他东西。

薄版 - 包含上述所有内容,以及应用程序的直接依赖项(数据库驱动程序,实用库等)。

空洞版 - 和薄版相反。它只包含运行应用程序所需的部分,但不包含应用程序本身。基本上是一个预打包的“应用服务器”,您可以稍后将应用程序部署到其中,与传统的Java EE应用服务器类似,但有重要的区别。

肥胖/超级版 - 包含您自己编写的部分加上您应用程序的直接依赖项以及使应用程序能够“独立运行”的组件。

来源:Dzone的文章

Visual representation of JAR types


17
我认为这是最佳答案,因为它还将此与其他类型的罐进行了比较。 - Grizz
1
“Hollow”难道不是“skinny”的反义词,而不是“thin”吗?“Thin”的反义词应该是你的代码中没有使用的所有依赖项。 :-) - greymatter
优美的解释 - vijayraj34
Java运行时能够支持运行Fat jar吗? - Nick Wills
@NickWills 是的,应该这样。 - Mark Han

116
胖jar是一个包含了你的项目所依赖的所有库的类以及当前项目的类的jar文件。
在不同的构建系统中,创建胖jar的方法也不同。例如,在Gradle中可以使用以下指令创建胖jar(instruction)。
task fatJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'com.example.Main'
    }
    baseName = project.name + '-all'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

在Maven中,这是通过以下方式完成的(在设置常规jar之后):
<pluginRepositories>
   <pluginRepository>
        <id>onejar-maven-plugin.googlecode.com</id>
        <url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
   </pluginRepository>
  </pluginRepositories>
<!-- ... -->

<plugin>
    <groupid>org.dstovall</groupid>
    <artifactid>onejar-maven-plugin</artifactid>
    <version>1.4.4</version>
    <executions>
        <execution>
            <configuration>
                <onejarversion>0.97</onejarversion>
                <classifier>onejar</classifier>
            </configuration>
            <goals>
                <goal>one-jar</goal>
            </goals>
        </execution>
   </executions>
</plugin>

33
“Fat jar”是否只是“uber jar”的另一个名称? - gturri
14
是的,确切地说。 - Dmitry Ginzburg
4
既然 Maven Assembly 插件提供了 jar-with-dependencies 组件,为什么还要使用某些第三方插件呢? - MeTTeO
3
@MeTTeO,你可以不使用插件添加自己的答案。 - Dmitry Ginzburg
3
我认为Uber-jar是捆绑概念的一种特定实现,而Fat jar只是这个概念本身。 - Sridhar Sarnobat
使用您所描述的Gradle中的fatJar是一个不好的想法:这样做,您将有大量重复的依赖项(如果有的话)。最好使用适当的插件:https://github.com/johnrengelman/shadow - WhiteAngel

20
肥厚的jar包或者叫超级jar包,是一种包含了项目所有类文件、资源和依赖项的jar包。实现这种效果有不同的方法:
  • 依赖项的jar包被复制到主jar包中,并且使用特殊的类加载器进行加载(例如:onejar、spring-boot-plugin:repackage)
  • 依赖项的jar包在主jar包层次结构的顶部被解压缩(例如:maven-assembly-plugin 与它的 jar-with-dependencies 方式装配)
  • 依赖项的jar包在主jar包层次结构的顶部被解压缩,并且它们的包名被重命名(例如:maven-shade-plugin 使用 shade goal)
下面是一个使用jar-with-dependencies方式进行装配的插件配置示例:
<project>
  ...
  <build>
    ...
    <plugins>
      <plugin>
        <!-- NOTE: We don't need a groupId specification because the group is
             org.apache.maven.plugins ...which is assumed by default.
         -->
        <artifactId>maven-assembly-plugin</artifactId>
        <version>2.6</version>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
          <classifier
        </configuration>
        ...
</project>

更详细的解释请参考:imagej.net上的Uber-JAR


2
使用FAT JAR时需要注意的一点是:不同依赖JAR包中相同类的冲突版本。根据你采用的方法(即完全将所有JAR包解压并重新组合成一个JAR包,还是使用JAR包的集合),你可能会得到非常不同(通常是非常令人沮丧的)效果。两种方法都不一定更好。大多数构建系统都有某种“反向依赖探测器”,可以提醒你这样的版本冲突。 - Charles Roth
是的,解压多个JAR文件有一些重要的缺点。另一个问题是META-INF文件,例如签名文件或SPI(services/package.Class)文件,这些文件默认情况下会被汇编插件覆盖。Shade插件具有一些特殊的转换器,可以在需要时合并这些文件。 - MeTTeO

13

对于可执行的jar文件,关于“fat jar”的另一种理解是可以通过调用来执行的jar文件:

java -jar myFatLibrary.jar

无需使用-cp / --classpath,甚至不需要双击jar图标。


2
请记住,-jar 命令需要 MANIFEST.MF 文件中的 Main-Class 头部信息:https://docs.oracle.com/javase/tutorial/deployment/jar/run.html - MeTTeO
1
即使对于非fat jar文件也是如此,因此并不相关。 - Sridhar Sarnobat
我们可以使用哪个mvn标志来跳过构建fat jar文件? - Alexander Mills
默认情况下它不会变胖。您必须明确使用jar-with-dependencies、uberjar或shadow来进行mvn安装,以便将除生成的类文件之外的任何内容放入其中。 - Sridhar Sarnobat

1

Gradle文档中:

在Java领域中,应用程序及其依赖项通常被打包为单个分发存档中的独立JAR文件。这仍然会发生,但现在有另一种常见的方法:将依赖项的类和资源直接放入应用程序JAR中,创建所谓的超级或胖JAR。

下面是在build.gradle文件中演示的uberJar任务:

task uberJar(type: Jar) {
    archiveClassifier = 'uber'

    from sourceSets.main.output

    dependsOn configurations.runtimeClasspath
    from {
        configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
    }
}

在这种情况下,我们正在获取项目的运行时依赖项 — configurations.runtimeClasspath.files — 并使用zipTree()方法对每个JAR文件进行封装。结果是一组ZIP文件树,其内容将与应用程序类一起复制到超级JAR中。

复制的内容需要更清楚地标记为引用。也就是说,要加上引号。 - Peter Mortensen
例如,可以参考*"如何引用他人撰写的资料"*。 - Peter Mortensen
否则,这是抄袭。 - Peter Mortensen

0

一个fat jar简单地包含了一个经典jar中的所有类+它们所有运行时依赖的类。

使用Jeka(https://jeka.dev),您可以以编程方式实现它:

JkPathTreeSet.of(Paths.get("classes")).andZips(
    Paths.get("bouncycastle-pgp-152.jar"),
    Paths.get("classgraph-4.8.41.jar"),
    Paths.get("ivy-2.4.0.jar")
).zipTo(Paths.get("fat.jar"));

或者只需通过参数化Java插件:

javaPlugin.getProject().getMaker().defineMainArtifactAsFatJar(true);

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