5

Java 8在这里。Gradle能以任何方式帮助解决jar地狱吗?

说有一个老版本的widget libray的,与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.4这是由fizzbuzz库被拉入,因为它的依赖;和
  2. widgetmakers:widget:2.0.0其中我直接引用

所以,很显然,这取决于其中Widget版本首先得到classloaded,我们要么有一个Widget#meowWidget#bark

Gradle是否提供任何帮助我的设施?有什么办法可以拉同一班的多个版本,并配置fizzbuzz班使用老版本Widget,我的班级使用新版本?如果没有,我能想到的唯一的解决方案是:

  1. 我也许能实现某种shading-和/或基于fatjar-soltuion的,其中或许我拉在我所有的依赖关系包myapp/bin下和然后给他们不同的版本前缀。无可否认,我在这里看不到一个明确的解决方案,但肯定有一些可行的(但完全哈克/讨厌)。或...
  2. 仔细检查我的整个依赖关系图,只是确保我的所有传递依赖不会相互冲突。在这种情况下,对于我来说,这意味着要么向fizzbuzz维护人员提交拉取请求,以便将其升级到最新的widget版本,或者可悲的是,将myapp降级为使用较旧的widget版本。

但是Gradle(到目前为止)对我来说已经是魔术了。所以我问:是否有任何Gradle魔法可以利用我?

+0

我看到你可以正确使用同一个库的两个不同版本的唯一可能的方法是通过引用你自己或库所需的每个版本。如果没有,那么你将不得不依赖于Gradle提供的版本。如果版本错误,你也会得到一个没有发现异常的类/方法。 Gradle可以找到缺少的依赖关系并将它们拉出来,但是通过将其与2个库混淆来手动破坏Gradle的依赖关系解析机制从长远来看不会有任何好处。 –

+0

谢谢@WeareBorg(+1)。 **(1)**当你说“*指自己或图书馆所需要的每个版本*”时,你能详细说明一下吗?我已经是JVM开发超过10年了,并且不熟悉这种策略/方法!你能举一个你的意思吗? **(2)**在这种情况下,Gradle的默认行为是什么?保持不动,哪个版本的'widget'库将Gradle最终检索/解析?再次感谢! – smeeb

+1

1)我的意思很简单,但是我知道maven的语法,在这里你可以排除你不需要的特定版本的库,所以其他版本会自动拖动。2)由于类路径的增加,它将成为列表中的第一个,对于maven,它将是最新的。 –

回答

5

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

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

以前所有这些路径都走了,我肯定会首先尝试阴影方法,如果这样做不起作用,OSGI,然后可能会留下手动加载器魔术作为最后的手段。 – biziclop

+0

@biziclop是的我同意这是最痛苦的方式 –

+0

似乎是一个棘手问题的最佳答案。 – cjstehno

2

Gradle只会建立与您的依赖关系的类路径,它不会提供自己的运行时来封装依赖关系及其传递依赖关系。在运行时激活的版本将是根据类加载规则的版本,我相信它是类路径顺序中的第一个包含类的jar。 OSGI提供了可以处理像这样的情况的运行时,以及即将到来的模块系统。

编辑:比约恩是正确的,它会尝试解决不同版本的冲突;它会根据它的策略编译类路径,所以你把依赖关系放在文件中的顺序并不重要。然而,你仍然只获得每个类名一个类,它不会解决OP的问题

+1

这是错误的。 Gradle将根据选择的冲突解决策略来解决冲突。默认情况下,它将使用所请求的所有版本的最新版本,除非以另一种方式配置Gradle e。 G。错误或使用特定的固定版本或类似的东西。只要除了版本之外的坐标相同,它就不会将两个版本都添加到类路径中。 – Vampire

0

如果您拥有不同版本的库,并且具有相同的坐标,则Gradles冲突解决机制将发挥作用。

默认分辨率策略是使用库的最新请求版本。您不会在依赖关系图中获得同一个库的多个版本。

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

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

0

更糟糕的情况是当同一班级出现在多个瓶子中时。这更隐蔽 - 查看来自Codahale和Dropwizard的指标瓶子,这两个罐子中的相同类别的不兼容版本。 gradle classpath-hell插件可以检测到这个恐怖。

相关问题