guava listenablefuture版本号9999.0-empty-to-avoid-conflict-with-guava的原因

检查源码时偶然发现有个版本号9999的jar包依赖:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar ,很好奇为什么搞个这样的版本号。

从maven的依赖管理说起

网上谈maven依赖管理的文章很多了,有兴趣详细了解可以自行百度一下。当有依赖传递过程中不同的parent使用了相同的子依赖时:

A依赖于B及C,而B又依赖于X、Y,而C依赖于D、M,D依赖于X、N则A除引B及C的依赖包下,还会引入D、X,Y,M,N的依赖包(一般情况下了,Maven可通过<scope>等若干种方式控制传递依赖)。

这里有一个需要特别注意的,假设B依赖于X的1.0版本,而D依赖于X的2.0版本,A究竟依赖于X的1.0还是2.0版本呢?

maven的原则是:

  1. 显示声明级别最高。如果版本号在parent的pom文件中显式声明过,明确指明我需要哪个版本,那么在之后声明的相同jar包版本都会被忽略。
  2. 最短路径优先。A->B->X三层,A->C->D->X四层,这时候A中使用的X版本为B中声明的1.0版。

这时候如果我们既不想在A中显式指定子依赖X的版本,又想用D的2.0版,那么可以考虑使用exclusions标签将C中的X依赖排除出去:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>A</artifactId>
    <version>0.0.1</version>
    <exclusions>
        <exclusion>
            <groupId>com.example</groupId>
            <artifactId>X</artifactId>
        </exclusion>
    </exclusions>
</dependency>

回到guava:listenablefuture的9999.0

这个奇怪的版本号的出现一部分也是因为想解决依赖冲突。该jar包自带的pom文件<description>中有相关说明:

An empty artifact that Guava depends on to signal that it is providing ListenableFuture -- but is also available in a second "version" that contains com.google.common.util.concurrent.ListenableFuture class, without any other Guava classes. The idea is:

- If users want only ListenableFuture, they depend on listenablefuture-1.0.

- If users want all of Guava, they depend on guava, which, as of Guava 27.0, depends on listenablefuture-9999.0-empty-to-avoid-conflict-with-guava. 

The 9999.0-... version number is enough for some build systems (notably, Gradle) to select that empty artifact over the "real" listenablefuture-1.0 -- avoiding a
conflict with the copy of ListenableFuture in guava itself. If users are using an older version of Guava or a build system other than Gradle, they may see class conflicts. If so, they can solve them by manually excluding the listenablefuture artifact or manually forcing their build systems to use 9999.0-....

虽然guava中也有ListenableFuture的实现,但是如果用户仅想用listenableFuture的话,可以仅依赖于版本号正常的 listenablefuture-1.0而无需使用任何其他guava相关依赖。

guava其优势在于整洁:把所有东西都放在了同一个依赖jar中,不会有乱七八糟的子jar造成复杂的依赖关系;但其问题也因此而生:该jar包太过庞大,可能会因为一个小功能点而引入一大堆不需要的鸡肋代码。

另一个问题在于,Google很多底层代码是共用的,例如Android和guava,com.google.common.util.concurrent.internal.InternalFutures路径一模一样,因此会报错路径冲突而无法编译通过。于是他们想出来这么一招:把listenableFuture独立出来,然后在两个地方分别引用,这样就可以让gradle, maven等工具通过依赖管理自动解决该问题,而且能单独仅使用listenableFuture而不引入guava那一大堆。

争议

参考链接:

https://softwareengineering.stackexchange.com/questions/390120/why-is-it-considered-bad-that-guava-introduced-direct-internal-dependencies

https://github.com/google/guava/issues/3320

这种做法当然存在争议。虽然路径冲突是硬伤当然需要解决,但这种解决方式一部分人认为是欠妥的:

  1. 解决的同时将core-jar包割裂开了,让core依赖于其他jar包的设计很奇怪(core应当视为最稳定的,理论上不该依赖任何其他的东西)。
  2. 有些项目需要列出全部依赖jar包,这时候需要费很大力气解释为什么这个jar包是空的,以及为什么版本号要用9999.0
  3. 一些相关的依赖包需要用exclusive手动排除解决冲突问题,这破坏了模块化设计
  4. 对于底层依赖而言,任何额外的依赖都会造成乘法级别的依赖复杂度增加(但9999只不过是个空jar,因此不存在部分依赖版本A,另一部分依赖版本B的问题)
  5. 实际上只是方便了那些需要用listenableFuture这一小块功能的人,让他们不需引入guava包

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值