【问题标题】:Should you provide dependent libraries in client jar?您应该在客户端 jar 中提供依赖库吗?
【发布时间】:2010-12-17 07:25:21
【问题描述】:

我们正在为其他内部应用提供客户端 jar,以连接到我们应用的 REST API。我们的 API 依赖于一些标准的 Jakarta 库。将这些 JAR 文件包含在我们的客户端 jar 文件中是最佳做法吗?还是您只是记录依赖关系,并由客户来确保他们的类路径中有这些 jar?

【问题讨论】:

    标签: java client-library


    【解决方案1】:

    您应该将任何第三方 jar 作为 uber jar 捆绑到您自己的 jar 中,但最好将您的发行版中所需的所有 jar 的副本包含在一个lib 目录或任何其他目录。

    主要原因是您的客户可能正在使用某种形式的依赖管理系统(maven/ivy 等),并且提供实际上不属于您的项目的包和类将使这些方案无效。

    还有一种替代方法,那就是使用 maven shade plugin 之类的东西将您的依赖项重新定位到您自己的包命名空间中。这当然有不利的一面,即您会增加库的代码大小,但有利的一面是,您几乎可以保证依赖项的版本,而不会影响您的客户可能正在使用的任何其他库。

    编辑:回应马库斯·莱昂的评论:

    无需捆绑/重新定位的可能解决方案:

    • 文档,确保记录您的依赖关系以及与以前版本的任何已知冲突 - 没有人真正阅读它
    • 通过依赖项管理系统分发您的库...例如 maven 或 ivy 存储库,这些允许您在一组非常特定的边界(包括上限)中记录您的依赖项是什么 - 仍然可以被您的客户覆盖知道他们正在这样做
    • 在 MANIFEST.MF 中添加 OSGi 信息 - 仅当您的客户端实际使用 OSGi 时才有用
    • 如果您的依赖项是使用 maven 构建的,或者在清单文件中包含版本信息,您可以编写某种检查例程来扫描这些类路径并检查其中的版本 - 有点极端

    最后,实际上很难确保你拥有你想要的依赖项,因为 java 是一种后期绑定的语言,你的依赖项(即使是捆绑的)可能会被在类路径上包含不同版本的人覆盖.

    注意:我最近度过了非常糟糕的一天,试图找出为什么我们的一个应用无法找到新版本的 log4j。原因:有人试图提供帮助,把它捆绑到一个随机的完全不相关的罐子里。

    【讨论】:

    • 如果不捆绑,并且客户端使用不同版本的dependent.jar,如何确保client.jar引用了正确版本的dependent.jar?
    • 查看编辑...简短的回答是你不能...即使你捆绑
    • 你的意思是即使将依赖项捆绑到清单中设置了类路径的jar中,编译器也不会加载类路径清单条目中明确提到的jar文件?跨度>
    • 是的..类加载器可以选择忽略清单条目(不确定 webapp 类加载器是否遵循它)或者类可能已经加载。
    • 所以基本上你必须交叉手指祈祷??如果您在 client.jar 中捆绑了dependency.jar,并且您的类路径中有一个不同版本的dependency.jar,您无法控制将使用哪个版本?
    【解决方案2】:

    您应该包含您所依赖的所有 jar。如果您允许客户端提供标准 jar,则您依赖它们提供正确的版本。如果您包含您知道您的应用可以使用的版本,这对你们双方来说都会轻松得多。

    【讨论】:

    • 如果您包含 jar 的版本 #2 并且客户端在类路径中已经有版本 #1,您如何强制您的库使用版本 #2(而不是 v #1)?跨度>
    • 您可以在应用程序 jar 的清单文件中指定应用程序的类路径。 (java.sun.com/docs/books/tutorial/deployment/jar/downman.html) CLASSPATH 定义(由环境变量定义)被 jar 清单定义的“类路径”覆盖。
    • 我读过 (one-jar.sourceforge.net) 说“Java 类加载器不知道如何从 Jar 中的 Jar 加载类。Class-Path 上的条目必须是对外部文件的引用Jar 文件,违背了在单个 Jar 文件中交付应用程序的目标。”
    • 我从未尝试将第三方 jar 捆绑到我的可执行 jar 文件中。您可以将它们放在 /lib 目录中,以便类加载器(使用您的 jar 清单)知道在哪里可以找到它们。
    【解决方案3】:

    捆绑到单个 jar 中的优点显然是:

    • 为客户提供简单性

    捆绑的缺点是:

    • 无法更新依赖库的版本
    • 隐藏必要的库实际上会导致客户端与那些额外的库发生潜在冲突
    • 构建过程对您来说很复杂(但在 Ant/Maven 中很容易做到这一点的方法)
    • 某些带有清单的库不能只是打开和重新打开 - 您需要实际合并清单信息。不过,Ant 和 Maven 对此提供了支持。

    另一种选择是使用单个主 jar(您的代码)并设置类路径清单属性来吸收其他 jar。我个人不喜欢这样,因为很容易错过这些半隐藏的依赖关系。

    最后,如果你只是说一两个罐子,我会把它们分开。如果您说的是 10 或 15,则可能需要考虑捆绑。

    【讨论】:

      【解决方案4】:

      如果您将应用程序部署为独立应用程序,则应包含这些 jar。如果您正在部署源代码以供某人构建并且您提供了一个 maven pom 文件,那么您不一定需要。

      【讨论】:

        【解决方案5】:

        您表达了对非常具体的库版本依赖关系的紧张情绪(我可以理解,考虑到 Hibernate 在其不同模块之间的紧密耦合程度——只有某些版本的 Hibernate-annotations 与一个 hibernate-core 一起工作,这又必须与特定的休眠验证器一起使用)。

        如果你知道一个包需要作为更大平台的一部分工作(例如,作为一个框架的插件,可能会加载它自己的 jars),你真的需要更强大的 OSGI 多版本类加载(又名 JSR-291): OSGI Technology

        对于支持 OSGI 的框架,如 Spring、Eclipse、Jonas 或 Glassfish,您可以在 XML 文件中指定您的特定版本依赖项,以及是否依赖于捆绑包 libfoo-1.1.4,而另一个插件依赖于libfoo-2.0a,OSGI 依赖管理器将确保每个加载正确的版本。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-09-14
          • 2017-01-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-17
          • 2014-03-02
          相关资源
          最近更新 更多