这会给你带来依赖问题。这是一个非常典型的依赖问题,不受 REST 或 Jersey 的约束。但是让我们看看下面的场景:
依赖关系
假设有两个服务器(S1 和 S2)、两个客户端(C1 和 C2)和两个库,其中包含用于访问服务器(L1 和 L2)的其余客户端代码。两个客户端都需要查询两个服务器,因此调用结构如下所示:
C1 ---> L1 ---> S1
\ ^
\ /
X
/ \
/ v
C2 ---> L2 ---> S2
此外,L1 和 L2 都依赖于 Jersey。此外,容器(也许您正在 Glassfish 或 Wildfly 应用服务器上运行您的应用程序)依赖于 Jersey(或至少依赖于 jax-rs API [1])。
C1的简化依赖结构如下:
C1
/ | \
/ | \
< v >
Container L1 L2
| | |
v v v
Jersey Jersey Jersey
只要三个版本的泽西都一样,一切都很好。没问题。但如果版本不同,您可能会遇到讨厌的NoClassDefFoundErrors、MethodNotFoundErrors 等 [2]。
刚性结构
如果版本足够相似,您将不会直接遇到这些错误。也许它会工作很长一段时间。但随后可能会发生以下情况:
- 您想要更新您的容器。这会更新可能不兼容的 Jersey 的容器版本。 ==> 繁荣。因此,使用 L1 和 L2 会阻止您进行更新。只要 L1 和 L2 没有更新,您就会被旧版本卡住。
- L2 已更新为新版本的 Jersey。只有在 L1 和您的容器也更新时,您才能使用它。所以你坚持旧版本(你可以这样做,因为 REST 的松散耦合)。但随后 S2 中添加了新功能,该功能仅适用于新版本的 L2,您再次陷入困境。
请记住,这些错误可能会发生也可能不会发生。不能保证你会遇到麻烦,也不能保证它会起作用。这是一种风险,一颗定时炸弹。
另一方面,这是一个只有两个服务和两个客户端的简单示例。当然,如果数量更多,风险就会增加。
稳定依赖原则
依赖项应遵守Stable Dependencies Principle (SDP),其内容为“依赖于稳定方向”。 Bob 大叔使用传入和传出依赖的数量来定义它,但 SDP 可以从更广泛的意义上理解。
当我们查看给定的依赖结构并将 Jersey 替换为 guava 时,与 SDP 的联系变得显而易见。这个问题在番石榴中很常见。问题是 Guava 通常不向下兼容。这是一个相当不稳定的库,可以在应用程序中使用(它是不稳定的),但你不应该在可重用的库中使用它(它应该是稳定的)。
对于泽西岛,这并不那么明显,因为您可能认为泽西岛相当稳定。事实上,就 Bob Martin 的定义而言,它是非常稳定的。但 L2 也可能相当稳定。也许它是由另一个团队开发的,由某个离开公司但没人敢碰它的人开发,因为去年有人试图这样做,导致 C2 团队出现依赖性问题。也许 L2 也很稳定,因为开发 C1 和 L2 的团队的经理之间存在一些持续的争吵,所以 L2 经理声称没有资源、没有预算或其他任何东西,所以只能在明年年底更新 L2。
因此,根据您的代码库、组织结构等,库可能比您想要的更稳定。
例外与补救措施
- 有些语言具有“孤立的依赖关系”,这意味着您可以在同一个应用程序中拥有同一个库的多个版本。
- 您可以重新打包 Jersey(复制源代码,更改包名称并自己维护)。然后,您可以在版本 X 中使用普通的 Jersey,在版本 Y 中使用重新打包的 Jersey 版本。这可行,但当然这有其自身的问题。工作量很大,而且您必须维护不是您编写的软件。
- 如果所有内容都在同一个团队中开发,则问题可以忽略不计。然后你就可以控制整个事情,而不需要依赖其他团队做某事。然而,一旦您的代码库增长,更新到新版本可能会很麻烦,因为您不能再逐步进行,但您必须一次将许多服务移动到新版本。
- 像 maven 这样的构建工具可以让您对所依赖的版本进行一些控制。您可以考虑在 L1 和 L2 中标记 Jersey 依赖项
optional,在 C1 和 C2 中标记 provided。然后它不包含在您的 war 文件中,因此只有一个 Jersey 版本(容器中的那个)。只要此版本与接口兼容,它就可以工作 [3]。
该怎么做
我建议将您的表示类(即 DTO)放入客户端 jar 中,让每个客户端决定使用哪个库来进行 REST 调用。通常,那些REST gateways 非常简单,在应用程序之间共享它们通常不值得冒险。至少在不知不觉中遇到这里提到的问题之前考虑一下。
[1] 为了使解释简单,让我们忽略 Jersey 和 jax-rs API 之间的区别。论据是一样的。
[2] 实际上它有点复杂。通常,您的构建工具(maven 等)将选择一个版本的 Jersey 并将其放入 war 文件中。另一个版本的 Jersey 与另一个版本“因冲突而省略”。只要版本兼容,就可以。因此,您最终会得到两个版本,一个在您的容器中,一个在您的 war 文件中。前一种情况下的接口兼容性应该足够了,剩下的两个版本就不够了。
[3] 因为只有一个版本,所以不会出现[2]中提到的问题。接口兼容性问题当然存在。