格拉德尔可以以任何方式帮助解决罐子地狱吗?



Java 8 在这里。

假设有一个旧版本的widget库,其中 Maven 坐标widgetmakers:widget:1.0.4,其中定义了一个类,如下所示:

public class Widget {
    private String meow;
    // constructor, getters, setters, etc.
}

岁月流逝。这个widget库的维护者决定Widget永远不应该meow,相反,它实际上应该bark。因此,发布了一个新版本,Maven坐标widgetmakers:widget:2.0.0Widget看起来像:

public class Widget {
    private Bark bark;
    // constructor, getters, setters, etc.
}

所以现在我去构建我的应用程序,myapp.而且,想要使用我所有依赖项的最新稳定版本,我像这样声明我的依赖项(在build.gradle内部(:

dependencies {
    compile (
        ,'org.slf4j:slf4j-api:1.7.20'
        ,'org.slf4j:slf4j-simple:1.7.20'
        ,'bupo:fizzbuzz:3.7.14'
        ,'commons-cli:commons-cli:1.2'
        ,'widgetmakers:widget:2.0.0'
    )
}

现在假设这个(虚构的(fizzbuzz一直依赖于 1.x 版本的widget库,Widgetmeow .

所以现在,我在编译类路径上指定了 2 个版本的 widget

  1. widgetmakers:widget:1.0.4fizzbuzz库拉入,作为它的依赖关系;和
  2. 我直接引用的widgetmakers:widget:2.0.0

所以很明显,根据Widget的哪个版本首先被类加载,我们将有一个Widget#meow或一个Widget#bark

Gradle是否提供任何设施来帮助我?有没有办法拉入同一类的多个版本,并将fizzbuzz类配置为使用旧版本的Widget,并将我的类配置为使用新版本?如果没有,我能想到的唯一解决方案是:

    我也许
  1. 能够完成某种基于着色和/或 fatjar 的解决方案,也许我将所有依赖项作为 myapp/bin 下的包拉入,然后为它们提供不同的版本前缀。诚然,我在这里没有看到明确的解决方案,但确信有些事情是可行的(但完全是黑客/讨厌的(。或。。。
  2. 仔细检查我的整个依赖关系图,并确保我所有的传递依赖关系不会相互冲突。在这种情况下,对我来说,这意味着要么向fizzbuzz维护者提交拉取请求以将其升级到最新的widget版本,要么可悲的是,降级myapp以使用较旧的widget版本。

但Gradle(到目前为止(对我来说是神奇的。所以我问:有什么格拉德尔魔法可以在这里使用我吗?

不知道 Gradle 的具体细节,因为我是 Maven 人,但无论如何这更通用。你基本上有两个选择(而且都是黑客(:

  1. 类加载器的魔术。不知何故,您需要说服构建系统加载两个版本的库(祝你好运(,然后在运行时,使用具有旧版本的 ClassLoader 加载使用旧版本的类。我已经这样做了,但这是一种痛苦。(像OSGI这样的工具可能会消除一些痛苦(
  2. 包底纹。重新打包使用旧版本库 B 的库 A,以便 B 实际上位于 A 内部,但具有特定于 B 的包前缀。这是常见的做法,例如 Spring 发布了自己的 asm 版本。在 Maven 方面,maven-shade-plugin 就是这样做的,可能有一个 Gradle 等价物。或者你可以使用ProGuard,800磅重的大猩猩Jar操纵。

Gradle 只会使用您的依赖项设置类路径,它不提供自己的运行时来封装依赖项及其传递依赖项。运行时处于活动状态的版本将根据类加载规则的版本,我相信这是类路径顺序中第一个包含类的 jar。 OSGI 提供的运行时可以处理此类情况,即将推出的模块系统也是如此。

编辑:Bjorn是对的,因为它将尝试解决不同版本中的冲突;它将根据其策略编译类路径,因此您将依赖项放在文件中的顺序无关紧要。但是你仍然每个类名只有一个类,它不会解决OP的问题

如果您有不同版本的库,其他坐标相等,Gradles 冲突解决机制就会发挥作用。

默认解析策略是使用最新请求的库版本。您不会在依赖项图中获得同一库的多个版本。

如果你真的需要在运行时使用同一库的不同版本,你必须做一些 ClassLoader 魔术,这绝对是可能的,或者对其中一个库或两者进行一些着色。

关于冲突解决,Gradle 内置了默认的最新策略和失败策略,如果依赖项图中存在不同版本,并且您必须显式解决构建文件中的版本冲突,则失败策略会失败。

更糟糕的情况是当同一个类出现在多个罐子中时。这更阴险 - 看看来自Codahale和Dropwizard的指标jar,以及两个jar中同一类的不兼容版本。gradle classpath-hell 插件可以检测到这种恐怖。

相关内容

  • 没有找到相关文章

最新更新