我们正在为其他内部应用程序提供一个客户端 jar,以连接到我们应用程序的 REST API。我们的 API 依赖于一些标准的 Jakarta 库。将这些 JAR 文件包含在我们的客户端 jar 文件中是最佳做法吗?还是您只是记录依赖关系,并由客户确保他们的类路径中有这些 jar?
5 回答
您不应将任何第三方 jar 作为 uber jar 捆绑到您自己的 jar 中,但最好将分发中所需的所有 jar 的副本包含在 lib 目录或其他任何目录中。
主要原因是您的客户可能正在使用某种形式的依赖管理系统(maven/ivy 等),并且提供实际上不属于您的项目的包和类将使这些方案无效。
还有一种选择,那就是使用类似maven shade 插件的东西将你的依赖项重新定位到你自己的包命名空间中。这当然有不利的一面,您会增加库的代码大小,但有利的一面是,您几乎可以保证依赖项的版本,而不会影响您的客户可能正在使用的任何其他库。
编辑:回应马库斯莱昂评论:
没有捆绑/搬迁的可能解决方案:
- 文档,确保您记录您的依赖关系以及与以前版本的任何已知冲突 - 没有人真正阅读它
- 通过依赖项管理系统分发您的库...例如 maven 或 ivy 存储库,这些允许您在一组非常特定的边界(包括上限)中记录您的依赖项是什么 - 仍然可以被覆盖,只是您的客户会知道他们正在做
- 在 MANIFEST.MF 中添加 OSGi 信息 - 仅在您的客户端实际使用 OSGi 时有用
- 如果您的依赖项是使用 maven 构建的,或者在清单文件中有版本信息,您可以编写某种检查例程来扫描类路径中的这些并检查那里的版本 - 有点极端
最后,实际上很难确保您拥有所需的依赖项,因为 java 是一种后期绑定的语言,您的依赖项(即使捆绑)可能会被包含在类路径上的不同版本的人覆盖。
注意:我最近度过了非常糟糕的一天,试图找出为什么我们的一个应用程序未能找到新版本的 log4j。原因:有人试图提供帮助,把它捆绑到一个随机的完全不相关的罐子里。
您应该包括您依赖的任何罐子。如果您允许客户端提供标准 jar,则您依赖它们提供正确的版本。如果您包含您知道您的应用程序可以使用的版本,那么这对你们双方来说都会更加轻松。
捆绑到单个 jar 中的优点显然是:
- 客户的简单性
捆绑的缺点是:
- 无法更新依赖库的版本
- 隐藏必要的库实际上会导致客户端与那些额外的库发生潜在冲突
- 构建过程中的复杂性(但在 Ant/Maven 中很容易做到这一点的方法)
- 一些带有清单的库不能只是 unjared 和 rejared - 您需要实际合并清单信息。不过,Ant 和 Maven 对此提供了支持。
另一种选择是使用单个主 jar(您的代码)并设置类路径清单属性来吸收其他 jar。我个人不喜欢这样,因为很容易错过这些半隐藏的依赖关系。
最后,如果你说的只是一两个罐子,我会把它们分开。如果您说的是 10 或 15,您可能需要考虑捆绑。
如果您将应用程序部署为独立应用程序,则应该包含这些 jar。如果您正在部署源代码以供某人构建并且您提供了一个 maven pom 文件,那么您不一定需要。
您对非常具体的库版本依赖关系表示紧张(我可以理解,考虑到 Hibernate 在其不同模块之间的紧密耦合程度——只有某些版本的 Hibernate-annotations 与一个 hibernate-core 一起工作,而后者反过来必须与特定的休眠验证器一起使用)。
如果您知道一个包需要作为更大平台的一部分工作(例如,作为可能加载自己的 jar 的框架的插件),那么您确实需要更强大的 OSGI 多版本类加载(又名 JSR -291): OSGI 技术
使用支持 OSGI 的框架,如 Spring、Eclipse、Jonas 或 Glassfish,您可以在 XML 文件中指定您的特定版本依赖项,以及是否依赖于捆绑包 libfoo-1.1.4,而另一个插件依赖于 libfoo-2.0 a,OSGI 依赖管理器将确保每个加载正确的版本。