4

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

我目前正在MyFirstTask 项目内package的文件中编写任务build.xml(将打包的构建文件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/目录下的依赖项?提前致谢!

4

5 回答 5

3

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

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

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

于 2012-09-30T14:59:05.847 回答
3

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

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

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

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

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

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

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

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

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

如果您确实采用第二条路线,那么为了不使用自动依赖项管理的用户的利益,还发布依赖项的 zip 可能是明智的。

于 2012-09-30T16:37:19.023 回答
3

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

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

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) JAR 文件仅包含发布到Maven 存储库的项目代码

优点

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

缺点

需要了解以下内容:


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

课程:在 JAR 文件中打包程序

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

JAR 文件格式提供了许多好处:

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

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

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

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

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

于 2012-09-30T15:24:55.653 回答
0

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

可能的问题:

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

AFAIK不可能在罐子里放罐子,否则就不可能为它们指定类路径。因此,重新包装程序。

于 2012-09-30T18:42:46.123 回答