与Maven一起处理依赖地狱的系统方法

时间:2015-11-25 01:45:06

标签: java maven intellij-idea pom.xml maven-dependency

我正在努力解决如何处理jar依赖地狱的问题。我有一个Maven-IntelliJ Scala项目,它使用了一些aws sdk' s。最近添加kinesis sdk引入了不兼容的Jackson版本。

我的问题是:我如何系统地解决Jar地狱的问题?

我理解类加载器以及maven如何在重复的Jars之间选择,但我仍然对解决问题的实际步骤感到茫然。

我目前的尝试是基于反复试验,我在杰克逊的例子中概述了这里:

  • 首先,我在Jackson数据绑定ObjectMapper类上看到了实际的异常,在本例中是NoSuchMethodError。然后,我查看Jackson文档,了解添加或删除方法的时间。这通常非常繁琐,因为我手动检查每个版本的api文档(问题1:有更好的方法吗?)
  • 然后,我使用mvn dependency:tree来确定我实际使用的Jackson版本(问题2:是否有一种自动方式询问maven正在使用哪个版本的jar,而不是梳理树输出?)
  • 最后,我在添加Kinesis SDK之前比较mvn dependency:tree输出,之后,检测mvn dependency:tree输出中的差异,并希望看看Jackson版本是否发生了变化。 (问题3:当发生依赖性解析时,maven如何使用阴影罐中的库?与其他任何一个相同?)

最后,在比较树输出后,我尝试在POM中明确添加最新的Jackson工作版本,以触发maven依赖解析链中的优先级。如果最新版本不起作用,我会添加下一个最新的lib,依此类推。

整个过程非常繁琐。除了我提出的具体问题,我也很好奇其他人对这个问题的系统方法。有没有人有他们使用的资源?

4 个答案:

答案 0 :(得分:18)

  

然后我查看Jackson文档,了解添加或删除方法的时间。这通常非常繁琐,因为我手动检查每个版本的api文档(问题1:有更好的方法吗?)

要检查API(破坏)兼容性,有几种工具可以自动分析jar并为您提供正确的信息。从this Stack Overflow帖子中可以找到一些方便工具的好提示 JAPICC似乎相当不错。

  

然后,我使用mvn dependency:tree来确定我实际使用的Jackson版本(问题2:是否有一种自动方式询问maven正在使用哪个版本的jar,而不是通过树输出?)

maven-dependency-tree绝对是可行的方法,但您可以从范围开始过滤掉,只能使用includes选项获取您实际需要的内容,如下所示:

mvn dependency:tree -Dincludes=<groupId>

注意:您还可以在includes表单中的groupId:artifactId:type:version选项中提供更多信息,或使用*:artifactId等通配符。

这似乎是一个小小的提示,但在具有许多依赖关系的大型项目中,缩小其输出量是非常有帮助的。通常,只需groupId就足够作为过滤器,*:artifactId可能是最快的,但如果您正在寻找特定的依赖项。

如果您对list依赖项(不是树)感兴趣,也按字母顺序排序(在许多情况下非常方便),那么以下内容也可能有所帮助:

mvn dependency:list -Dsort=true -DincludeGroupIds=groupId
  

问题3:当依赖项解析发生时,maven如何使用着色jar中的库?和其他任何一样?

通过阴影罐你可能意味着:

  • 胖罐,它还将其他罐子带入类路径。在这种情况下,它们被视为一个依赖项,Maven Dependency Mediation的一个单元,其内容将成为项目类路径的一部分。一般来说,你不应该把胖子作为依赖的一部分,因为你无法控制它带来的打包库。
  • 带阴影(重命名)包的罐子。在这种情况下 - 再次 - 就Maven依赖调解而言,没有任何控制权:它是一个单位,一个jar,基于其GAVC(GroupId,ArtifactId,Version,Classifier),使其独一无二。然后将其内容添加到项目类路径中(根据依赖项scope,但由于其程序包已重命名,因此可能难以处理冲突。再次,您不应该重命名包作为项目依赖项的一部分(但通常你不能知道)。
  

是否有人拥有他们使用的资源?

通常,您应该了解良好的how Maven handles依赖关系,并使用它提供的资源(其工具和机制)。下面是一些重点:

  • dependencyManagement绝对是本主题的入口点:在这里,您可以处理Maven依赖关系调解,影响其对传递依赖关系,其版本及范围的决策。重要的一点是:您添加到dependencyManagement的内容不会自动添加为依赖项。只有在项目的某个依赖项(如dependencyManagement文件中声明或通过传递依赖项)与其中一个条目匹配时,才会考虑pom.xml,否则将忽略它。它是pom.xml的重要组成部分,因为它有助于管理依赖关系及其传递图,以及为什么经常在父poms中使用:您只想处理一个并以集中方式处理您希望在所有Maven项目中使用哪个版本的log4j,在公共/共享父pom及其dependencyManagement中声明它,并确保它将被用作此类。集中化意味着更好的治理和更好的维护。
  • dependency部分非常重要用于声明依赖项:通常,您应该在此处仅声明所需的直接依赖项。一个好的重击规则是:在这里声明compile(默认)范围只是你在代码中实际用作import语句的内容(但是你经常需要超越它,例如,需要JDBC驱动程序在运行时并且从未在代码中引用,然后它将在runtime范围内)。还要记住:声明的顺序很重要:第一个声明的依赖关系会在与传递依赖冲突的情况下获胜,因此通过重新声明一个依赖关系,您可以有效地影响依赖关系中介。
  • 不要滥用依赖关系中的exclusions 来处理传递依赖关系:如果可以,请使用dependencyManagementdependencies的顺序。滥用exclusions会使维护变得更加困难,只有在您真正需要时才使用它。此外,在添加exclusions时,请始终添加XML注释,解释原因:您的团队成员或/和您未来的自我将会欣赏。
  • 谨慎使用依赖关系scope 。使用默认(compile)范围来确定编译和测试的真正需要(例如loga4j),仅使用test(仅限)测试中使用的内容(例如{ {1}}),请注意目标容器已经提供的junit范围(例如provided),仅将servlet-api范围用于运行时所需的范围,但您应该永远不要用它编译(例如JDBC驱动程序)。不要使用runtime范围,因为它只会带来麻烦(例如,它不会与您的最终工件打包在一起)。
  • 不要使用version ranges ,除非出于特定原因并且发现默认情况下指定的版本是最低要求,system表达式是最强的一个,但你很少需要它。
  • 使用Maven [<version>]作为库的property元素的占位符,以确保您有一个集中的位置来对一组依赖项进行版本控制都将具有相同的版本值。一个典型的例子是用于多个依赖项的versionspring.version属性。同样,集中化意味着更好的治理和维护,这也意味着更少的头痛和更少的地狱
  • 提供后,import BOM 作为上述要点的替代方案,并更好地处理依赖项系列(例如jboss),委托给另一个hibernate.version文件管理一组依赖关系。
  • 不要使用pom.xml依赖(或尽可能少)。如果您真的需要,请确保您永远不会使用SNAPSHOT依赖项发布:否则构建重现性将处于高危险状态。
  • 进行问题排查时,请务必检查SNAPSHOT文件的完整层次结构,在检查有效pom.xml时,使用help:effective-pom可能非常有用{{1}就最终的依赖图而言,并且dependencyManagement
  • 使用其他一些Maven插件来帮助您完成治理dependencies在故障排除过程中非常有用,但properties也可以提供帮助。以下是一些值得一提的例子:

以下示例将确保没有人(您,您的团队成员,您自己的未来)将能够在maven-dependency-plugin范围内添加一个众所周知的测试库:构建将失败。它确保maven-enforcer-plugin永远不会达到PROD(与compile打包在一起,例如。)

junit

看一下这个插件提供的其他standard rules:如果出现错误的情况,许多可能对破坏构建很有用:

同样,公共parent pom可以包含多个这些机制(war,enforcer插件,依赖项系列的属性),并确保遵守某些规则。您可能无法涵盖所有​​可能的情况,但它肯定会降低您感知和体验的地狱的程度。

答案 1 :(得分:5)

使用Maven Helper plugin通过排除旧版本的依赖项轻松解决所有冲突。

答案 2 :(得分:3)

根据我的经验,我没有发现任何完全自动化的东西,但我发现以下方法对我自己很有吸引力:

首先,我尝试使用项目结构的清晰映射,项目之间的关系,我通常使用Eclipse图形依赖关系视图,这告诉我,例如,是否省略了与另一个关系的冲突。 此外,它告诉您已解决的项目依赖项。 我真诚地不使用IntelliJ IDEA,但我相信它有类似的功能。

通常我会尝试在结构中放置更高的常见依赖关系,并利用<dependencyManagement>功能来处理传递依赖关系的版本,最重要的是,避免重复项目结构。

在这个Maven - Manage Dependencies blog post中,你可以找到一个关于依赖管理的好教程。

在我的项目中添加新的依赖项时,就像你的情况一样,我会在项目结构中添加它的位置并相应地进行更改,但在大多数情况下,依赖项管理机制能够处理这个问题。

在此Maven Best Practices blog post中,您可以找到:

  

Maven的dependencyManagement部分允许父pom.xml定义   可能在子项目中重用的依赖项。这个   避免重复;没有dependencyManagement部分,每个   子项目必须定义自己的依赖项并复制   版本,范围和依赖类型。

显然,如果您需要项目的特定版本的依赖项,您始终可以在层次结构深处指定本地需要的版本。

我同意你的观点,这可能相当繁琐,但依赖管理可以给你一个很好的帮助。

答案 3 :(得分:0)

即使替换所有具有相同名称的jar,您仍然可以拥有一些具有相同完全限定名称的类。我在我的一个项目中使用了maven shade插件。它打印来自不同jar的具有相同限定名称的类。也许这可以帮到你