你应该在客户端jar中提供依赖库吗?

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

你不应该将任何第三方jar子作为超级jar子捆绑到你自己的jar子里,但最好包括你的发行版所需的所有jar子的副本,比如说在lib目录中或者说是什么。

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

有一种替代方法是使用类似maven shade插件的东西将依赖项重定位到您自己的包命名空间中。 这当然有一个缺点,你将增加库的代码大小,但从好的方面来说,你可以几乎保证你的依赖项的版本,而不影响你的客户可能正在使用的任何其他库。

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

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

  • 文档,确保您记录您的依赖项以及与以前版本的任何已知冲突 – 没有人实际读取它
  • 通过依赖管理系统分发您的库…像maven或ivy repo,这些允许您在非常具体的边界(包括上层)中记录您的依赖项 – 仍然可以覆盖您的客户将知道他们正在做
  • 在MANIFEST.MF中添加OSGi信息 – 仅在客户实际使用OSGi时才有用
  • 如果您的依赖项是使用maven构建的,或者在那些清单文件中有版本信息,您可以编写某种检查例程来扫描类路径以查找这些并检查那些版本 – 有点极端

最后,实际上确保你拥有所需的依赖是非常困难的,因为java是一种后期绑定语言,有可能让你的依赖项(即使捆绑)被包含在类路径之前的不同版本的某个人覆盖。

注意:我最近花了很多时间试图找出为什么我们的某个应用程序找不到新版本的log4j。 原因:有人试图将其捆绑成一个随机完全无关的jar子。

你应该包括你所依赖的任何jar子。 如果您允许客户端提供标准jar,您可以依赖它们来提供正确的版本。 如果你包含你知道你的应用程序使用的版本,那么对你们来说都是非常轻松的。

捆绑到一个jar子的优点显然是:

  • 简单的客户端

捆绑的缺点是:

  • 无法更新依赖库的版本
  • 隐藏必要的库实际上可能导致客户端与这些额外库的潜在冲突
  • 在构建过程中你的复杂性(但在Ant / Maven中很容易做到这一点)
  • 一些带有清单的图书馆不能不被疏忽和重新调整 – 您需要实际合并清单信息。 虽然有Ant和Maven的支持。

另一种选择是使用单个主jar(您的代码),并将class-path manifest属性设置为吸入其他jar。 我个人不喜欢这个,因为很容易错过这些半隐藏的依赖。

最后,如果你只说一两个jar子,我会把它们分开。 如果您正在谈论10或15,您可能需要考虑捆绑。

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

你对非常具体的库版本依赖关系表达了​​紧张(我可以理解,考虑到Hibernate在不同模块之间的紧密耦合 – 只有某些版本的Hibernate-annotations与一个hibernate-core一起使用,必须与特定的hibernate-validator一起使用)。

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

使用支持OSGI的框架(如Spring,Eclipse,Jonas或Glassfish),您可以在XML文件中指定特定的版本依赖项,如果依赖于bundle libfoo-1.1.4,则另一个插件依赖于libfoo-2.0 a,OSGI依赖关系管理器将确保每个加载正确的版本。