【问题标题】:Is it correct or incorrect for a Java JAR to contain its own dependencies?Java JAR 包含自己的依赖项是正确还是错误?
【发布时间】:2012-09-21 15:22:35
【问题描述】:

我想这是一个由两部分组成的问题。我正在尝试编写我自己的 Ant 任务 (MyFirstTask),它可以在其他项目的 build.xml 构建文件中使用。为此,我需要将我的 Ant 任务编译并打包到它自己的 JAR 中。因为我写的这个 Ant 任务相当复杂,它有大约 20 个依赖项(其他 JAR 文件),例如使用 XStream 进行 OX-mapping,Guice 进行 DI 等。

我目前正在 MyFirstTask 项目内的build.xml 文件中编写package 任务(将打包myfirsttask.jar 的构建文件,这是可重用的Ant 任务)。

我突然意识到我并不完全理解 Java JAR 的意图。 JAR 是否应该包含依赖项,并将其留给运行时配置(应用程序容器、运行时环境等)来为其提供所需的依赖项?我会假设如果是这种情况,可执行 JAR 是规则的例外,是吗?

或者,Java JAR 是否意图也包含它们的依赖项?

无论哪种方式,我都不想强迫我的用户将 25 个以上的 JAR 复制粘贴到他们的 Ant 库中;这太残忍了。我喜欢 WAR 文件的设置方式,其中依赖项的类路径定义在 classes/ 目录下。

我想,最终,我希望我的 JAR 结构看起来像:

myfirsttask.jar/
    com/  --> the root package of my compiled binaries
    config/  --> config files, XML, XSD, etc.
    classes/  --> all dependencies, guice-3.0.jar, xstream-1.4.3.jar, etc.
    META-INF/
        MANIFEST.MF

假设为了完成这个(并让运行时类路径也查看classes/ 目录),我需要以某种方式修改 MANIFEST.MF(我知道有我相信一个名为ClassPath 的清单属性?)。我只是很难将所有东西放在一起,并且对 JAR 的最初意图有一个迫在眉睫/挥之不去的问题。

有人可以确认 Oracle 是否打算让 JAR 包含它们的依赖项吗?而且,无论哪种方式,我必须在清单(或其他任何地方)中做什么,以确保在运行时,类路径可以找到存储在 classes/ 目录下的依赖项?提前致谢!

【问题讨论】:

  • 您可能在这里遗漏的一个细节是这是针对 EE 容器还是独立应用程序。对此没有正确答案,但在容器中肯定有错误的答案。罐子可以包含依赖项,所以是的,这是有意的。是好的设计吗?并非总是如此。
  • 使用 ivy 满足依赖关系的完整示例可能会有所帮助。请参阅this answer,它适用于script 任务。

标签: java ant jar classpath manifest.mf


【解决方案1】:

Java JAR 包含自己的依赖项是正确还是错误?

在某些用例中,JAR 文件包含自己的文件是正确的 依赖关系。如果您想支持不使用现代的用户 依赖管理,您可能需要提供一个包含您的 Ant 的 JAR 文件 任务代码以及所有依赖项。更强大、更灵活、更模块化 方法是将版本化的 JAR 文件发布到 Maven repository 仅包含您的项目代码。

1) 包含项目代码和所有依赖项的 JAR 文件

优点

构建 JAR 的示例 Ant 目标

<target name="jar" depends="compile"
    description="Creates a standalone JAR of all class files and dependencies.">
  <jar destfile="${my.ant.task.jar.file}" update="true">
    <fileset dir="${build.classes.dir}" />
    <zipfileset src="${lib.dir}/javax.inject.jar" />
    <zipfileset src="${lib.dir}/guice-3.0.jar" />
    <zipfileset src="${lib.dir}/guice-multibindings-3.0.jar" />
    <zipfileset src="${lib.dir}/guice-assistedinject-3.0.jar" />
  </jar>
</target>

缺点

  • 如果您的 Ant 任务的最终用户已经拥有部分或全部依赖项 包含在他们的项目中,那么他们最终会得到 依赖关系
  • JAR 文件可能非常大

2) 仅包含发布到Maven Repository 的项目代码的 JAR 文件

优点

  • 用户可以获取您发布到的任何版本的 Ant 任务 Maven repository,它提供了更多的灵活性来发布新的 任务的版本,同时允许现有用户继续使用以前的 避免可能的回归的版本
  • 避免常见依赖项的重复副本(不同版本的依赖项导致错误除外)
  • JAR 文件会很小

缺点

需要了解以下内容:


作为参考,Java™ 教程提供了一个很好的 JAR 文件摘要。

Lesson: Packaging Programs in JAR Files

Java™ Archive (JAR) 文件格式使您能够捆绑多个文件 到单个存档文件中。 JAR 文件通常包含类文件和 与...相关的辅助资源... 应用程序

JAR 文件格式有很多好处:

  • 安全性:您可以对 JAR 文件的内容进行数字签名...
  • 减少下载时间:如果您的小程序捆绑在 JAR 中...
  • 压缩:JAR 格式允许您压缩文件以提高效率 存储。
  • 为扩展打包:扩展框架提供了一种方法 您可以将功能添加到 Java 核心平台和 JAR 文件 格式定义了扩展的包装...
  • 包密封:存储在 JAR 文件中的包可以选择密封,以便 该软件包可以强制执行版本一致性。将包装密封在一个 JAR 文件意味着该包中定义的所有类必须在 相同的 JAR 文件。
  • Package Versioning:一个 JAR 文件可以保存它所包含的文件的数据, 例如供应商和版本信息。
  • 可移植性:处理 JAR 文件的机制是 Java 平台的核心 API。

【讨论】:

    【解决方案2】:

    一些java应用程序供应商使用以下场景来分发他们依赖于其他jar的应用程序,这让人想起静态链接。在构建 jar 的阶段,所有依赖项(也是 jar)都被解包。在构建最终的 jar 时,它们包括新编译的类和从依赖项中提取的类。

    可能的问题:

    1. 应用程序无法重用这些库,因为它们包含在应用程序中。常见的静态链接问题。
    2. 必须遵守重新打包库的许可。通常可以重新打包它们,但有时必须额外注意它们的许可证文件,这些文件可能恰好在它们的 jar 中。

    AFAIK 无法在 jar 中包含 jar,否则无法为它们指定类路径。因此是重新包装程序。

    【讨论】:

      【解决方案3】:

      “JAR 文件”一词至少可以表示两件事,或者更确切地说,它的含义至少有两个方面。基本上,它意味着一种容器格式:基本上,一个带有 META-INF 目录的 ZIP 文件。更准确地说,它意味着这个容器被用作打包类文件的一种方式。

      在作为容器的意义上,没有关于内容的意图;该文件可能包含类文件、其他 JAR(在任何一种意义上!)等。但就作为代码的打包而言,我相信 JAR 文件的目的是让它们不包含任何依赖项。

      如果您阅读过JAR File Specification,您会发现有几个关于类文件存储的典故,但没有关于存储其他JAR 文件的内容。相应地,如果您查看 JRE 中 JAR 文件类加载器的实现,它无法对嵌套的 JAR 做任何有用的事情。

      此外,JAR 规范确实详细说明了处理非嵌套依赖项的机制:the Class-Path attribute。这允许 JAR 文件对文件系统中的其他 JAR 文件进行相对引用。

      现在,包装意义上的 JAR 文件并不是容器意义上的 JAR 文件的唯一用途。 WAR、EAR 和 RAR 文件(以及更多)都是用于特定目的的 JAR 文件。这些中的每一个都能够包含其他 JAR:WAR 可以包含打包 JAR 文件,EAR 可以包含这些 JAR 文件,也可以包含 WAR。然而,这些与打包 JAR 文件完全不同。值得注意的是,需要使用 Java 标准库中没有的特殊类加载器才能使用它们。

      WAR 等可以将许多 JAR 文件收集在一起的方式确实非常有用,而且在 Java EE 之外的 Java 中没有通用的机制来执行此操作,这真是太可惜了。如果有一个简单地捆绑了一些 JAR 的“应用程序存档”或“元存档”格式,那就太好了。

      因此,您会遇到用户需要 25 个 JAR 才能使用您的插件的问题。您大致有两种选择。

      首先,您接受痛苦,并将您的插件作为一个包含 JAR 的 zip 文件分发,用户必须将其解压缩。

      其次,你加入了 21 世纪,并使用自动处理依赖关系的构建工具和分发机制:在实践中,这意味着使用 Gradle、Maven 或其他工具(如 Ant)与 Ivy 配合使用,以从 Maven Central 获取依赖项,然后发布您的代码以及列出这些依赖项的 POM 文件。然后用户可以下载你的 JAR 和 POM,并让他们自己的构建工具获取依赖项。

      如果您确实采用了第二条路线,那么为了不使用自动依赖项管理的用户的利益,最好也发布依赖项的 zip。

      【讨论】:

        【解决方案4】:

        “Jar Jar Links”仅适用于独立应用程序。但不适用于蚂蚁。 如果您的项目具有相同的依赖项并且它们稍后升级到较新的版本,例如 xstream-*.jar,那么就会发生冲突,并且可能会选择错误的版本。在最坏的情况下会出现 MethodNotFoundException。这就是为什么在单个 jar 中包含依赖项是一种不好的做法。

        “我不想强迫我的用户复制粘贴 25 个以上的 JAR”有什么问题?

        这是最简单的解决方案。最好的,因为您将避免将来出现问题。

        现在,当您看到 Ant 的不便之处时,您可能想将其与 Gradle 进行比较。使用 Gradle,您获得的任务有点类似于 Ant,并且您不需要提供任何依赖项 jar。您的所有依赖项都将解析 Gradle。就像在 Ant 中一样,您仍然可以创建自己的任务。

        【讨论】:

          【解决方案5】:

          目的 (AFAIU) 是为了让 JAR 文件的行为类似于本机代码共享对象文件(Unix 上的.so,Windows 上的.dll)。通常,应用程序将安装几个共享对象文件作为同级文件,以及用于启动它们的可执行文件。

          可执行 JAR 更像是一个独立的可执行文件,因此包含所有依赖项更为常见(类似于静态链接的本机代码可执行文件直接包含其所有依赖对象的方式)。

          不幸的是,默认的 ClassLoader 无法从嵌套的 JAR 中加载类。可以编写一个 ClassLoader。或者你可以使用别人写的。从您的问题描述来看,Jar Jar Links 正是您要查找的内容。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-10-27
            • 1970-01-01
            • 2023-03-14
            • 1970-01-01
            • 2023-03-27
            • 1970-01-01
            • 1970-01-01
            • 2017-04-30
            相关资源
            最近更新 更多