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

时间:2016-04-29 14:03:00

标签: java gradle dependency-management transitive-dependency conflicting-libraries

Java 8。

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

public class Widget {
    private String meow;

    // constructor, getters, setters, etc.
}
几年过去了。此widget库的维护者决定Widget永远不应该meow,而应该确实barkwidgetmakers:widget:2.0.0因此,制作了一个新版本,Maven坐标为Widgetpublic 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

现在让我们假设这个(虚构的)widget库的总是依赖于Widget库的1.x版本,其中meow会{ {1}}。

现在,我在编译类路径中指定了widget的两个版本:

  1. widgetmakers:widget:1.0.4fizzbuzz库引入,作为其依赖项;和
  2. 我直接引用的
  3. widgetmakers:widget:2.0.0
  4. 很明显,根据首先加载Widget版本的Widget#meow,我们会有Widget#barkfizzbuzz

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

    1. 我可能能够完成某种基于着色和/或基于fatjar的解决方案,或许我将所有依赖项作为fizzbuzz下的包引入,然后为它们提供不同的版本前缀。诚然,我在这里看不到明确的解决方案,但我确信一些事情是可行的(但完全是hacky / nasty)。或...
    2. 仔细检查我的整个依赖关系图,并确保我的所有传递依赖关系不会相互冲突。在这种情况下,对我来说,这意味着向widget维护者提交拉取请求以将其升级到最新的myapp版本,或者遗憾的是,降级widget以使用旧client.windowPosition('current', (res) => { console.log(res.value.x, res.value.y); }); {1}}版本。
    3. 但是Gradle(到目前为止)对我来说是神奇的。所以我问:是否有任何Gradle魔法可以在这里使用?

4 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:2)

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

编辑:Bjorn是正确的,它会尝试解决不同版本的冲突;它会根据策略编译类路径,因此将依赖项放在文件中的顺序并不重要。但是,每个类名仍然只能获得一个课程,它不会解决OP的问题

答案 2 :(得分:0)

如果你有一个坐标相同的库的不同版本,Gradles冲突解决机制就会发挥作用。

默认解决方案策略是使用最新请求的库版本。您不会在您的dependendcy图中获得同一个库的多个版本。

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

关于冲突解决方案,Gradle内置了默认的最新策略和失败策略,如果不同版本在依赖关系图中,则必须明确解决构建文件中的版本冲突。

答案 3 :(得分:0)

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