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

wor*_*138 20 java intellij-idea pom.xml maven maven-dependency

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

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

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

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

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

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

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

A_D*_*teo 19

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

为了检查API(破坏)兼容性,有几种工具可以自动分析罐子并为您提供正确的信息.从这个 Stack Overflow帖子中可以看到一些方便的工具.
JAPICC似乎相当不错.

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

maven-dependency-tree绝对是要走的路,但是你可以从范围开始过滤掉,只能得到你真正想要的东西,使用它的includes选项如下:

mvn dependency:tree -Dincludes=<groupId>
Run Code Online (Sandbox Code Playgroud)

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

这似乎是一个小小的提示,但在具有许多依赖性的大型项目中,缩小其输出量是非常有帮助的.通常情况下,只要它groupId应该足够作为过滤器,*:artifactId但如果您正在寻找特定的依赖项,则可能是最快的.

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

mvn dependency:list -Dsort=true -DincludeGroupIds=groupId
Run Code Online (Sandbox Code Playgroud)

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

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

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

有没有人有他们使用的资源?

通常,您应该很好地理解Maven如何处理依赖关系并使用它提供的资源(其工具和机制).以下一些要点:

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

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

<plugin>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>1.4.1<.version>
    <executions>
        <execution>
            <id>enforce-test-scope</id>
            <phase>validate</phase>
            <goals>
                <goal>enforce</goal>
            </goals>
            <configuration>
                <rules>
                    <bannedDependencies>
                        <excludes>
                            <exclude>junit:junit:*:*:compile</exclude>
                            <exclude>org.mockito:mockito-*:*:*:compile</exclude>
                            <exclude>org.easymock:easymock*:*:*:compile</exclude>
                            <exclude>org.powermock:powermock-*:*:*:compile</exclude>
                            <exclude>org.seleniumhq.selenium:selenium-*:*:*:compile</exclude>
                            <exclude>org.springframework:spring-test:*:*:compile</exclude>
                            <exclude>org.hamcrest:hamcrest-all:*:*:compile</exclude>
                        </excludes>
                        <message>Test dependencies should be in test scope!</message>
                    </bannedDependencies>
                </rules>
                <fail>true</fail>
            </configuration>
        </execution>
    </executions>
</plugin>
Run Code Online (Sandbox Code Playgroud)

看看这个插件提供的其他标准规则:在出现错误情况时,许多可能对破坏构建很有用:

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


Meo*_*Meo 6

使用Maven Helper插件可以通过排除旧版本的依赖项轻松解决所有冲突.