Gradle能以任何方式帮助解决jar地狱吗?

sme*_*eeb 7 java conflicting-libraries dependency-management gradle transitive-dependency

Java 8在这里.

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

public class Widget {
    private String meow;

    // constructor, getters, setters, etc.
}
Run Code Online (Sandbox Code Playgroud)

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

public class Widget {
    private Bark bark;

    // constructor, getters, setters, etc.
}
Run Code Online (Sandbox Code Playgroud)

所以现在我去构建我的应用程序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'
    )
}
Run Code Online (Sandbox Code Playgroud)

现在,让我们说,这(虚构的)fizzbuzz图书馆总是对的1.x的版本取决于widget图书馆,在那里Widgetmeow.

所以现在,我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,然后为它们提供不同的版本前缀.诚然,我在这里看不到明确的解决方案,但我确信一些事情是可行的(但完全是hacky/nasty).要么...
  2. 仔细检查我的整个依赖关系图,并确保我的所有传递依赖关系不会相互冲突.在这种情况下,对我来说,这意味着向fizzbuzz维护者提交拉取请求以将其升级到最新widget版本,或者遗憾的是,降级myapp使用旧widget版本.

但Gradle(到目前为止)对我来说是神奇的.所以我问:是否有任何Gradle魔法可以在这里利用我?

Sea*_*oyd 5

不知道Gradle的具体细节,因为我是Maven的人,但无论如何这更为通用.你基本上有两种选择(两者都是hacky):

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