然后我查看 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>
注意:您还可以以groupId:artifactId:type:version 的形式向includes 选项提供更多信息,或使用*:artifactId 等通配符。
这似乎是一个小提示,但在具有许多依赖项的大型项目中,缩小其输出范围非常有帮助。通常,只需 groupId 作为过滤器就足够了,如果您正在寻找特定的依赖项,*:artifactId 可能是最快的。
如果您对 list 的依赖项(而不是树)也按字母顺序(在许多情况下非常方便)感兴趣,那么以下内容也可能会有所帮助:
mvn dependency:list -Dsort=true -DincludeGroupIds=groupId
问题 3:当依赖解析发生时,maven 如何使用阴影 jar 中的库?和其他人一样吗?
你可能指的是带阴影的罐子:
-
fat jars,这也将其他 jars 带入类路径。在这种情况下,它们被视为一个依赖项,Maven Dependency Mediation 的一个单元,其内容将成为项目类路径的一部分。一般来说,您不应该将 fat-jars 作为依赖项的一部分,因为您无法控制它带来的打包库。
-
带有阴影(重命名)包的罐子。在这种情况下 - 再次 - 就 Maven 依赖中介而言没有控制:它是一个单元,一个 jar,基于其 GAVC(GroupId,ArtifactId,Version,Classifier),这使其独一无二。然后将其内容添加到项目类路径中(根据依赖项scope,但由于其包已重命名,您可能会遇到难以处理的冲突。同样,您不应该将重命名包作为项目依赖项的一部分(但通常你不知道这一点)。
任何人都有他们使用的任何资源吗?
一般来说,您应该很好地理解how Maven handles 依赖关系并使用它提供的资源(它的工具和机制)。以下是一些要点:
-
dependencyManagement 绝对是这个话题的切入点:在这里你可以处理 Maven 依赖中介,影响它对传递依赖、它们的版本、它们的范围的决定。重要的一点是:您添加到 dependencyManagement 的内容不会自动添加为依赖项。 dependencyManagement 仅在项目的某个依赖项(如在pom.xml 文件中声明或通过传递依赖项中声明的)与其条目之一匹配时才考虑在内,否则它将被简单地忽略。它是pom.xml 的重要组成部分,因为它有助于管理依赖关系及其传递图,这就是为什么在父 pom 中经常使用的原因:您只想以集中方式处理一个版本,例如 log4j 您想要在所有的 Maven 项目中使用,你在一个公共/共享的父 pom 和它的dependencyManagement 中声明它,并确保它会被这样使用。集中化意味着更好的治理和更好的维护。
-
dependency 部分对于声明依赖项很重要:通常,您应该只在此处声明您需要的直接依赖项。一个很好的重击规则是:在此处声明为compile(默认值)范围仅是您在代码中实际使用的import 语句(但您通常需要超出此范围,例如,运行时需要的 JDBC 驱动程序,从不在您的代码中引用,然后它将在runtime 范围内)。还要记住:声明的顺序很重要:第一个声明的依赖在与传递依赖冲突的情况下获胜,因此通过明确地重新声明依赖,您可以有效地影响依赖中介。
-
不要在依赖项中滥用
exclusions 来处理传递依赖项:如果可以,请使用dependencyManagement 和dependencies 的顺序。滥用exclusions 会使维护变得更加困难,只有在确实需要时才使用它。此外,在添加 exclusions 时,请始终添加 XML 注释来解释原因:您的队友或/和您未来的自己会欣赏的。
-
慎重使用依赖项
scope。使用默认 (compile) 范围作为编译和测试真正需要的范围(例如 loga4j),仅(且仅)将 test 用于测试中使用的范围(例如 junit),请注意provided 范围用于您的目标容器已经提供的内容(例如servlet-api),仅将runtime 范围用于您在运行时需要的内容,但您不应该使用它进行编译(例如 JDBC 驱动程序)。不要使用 system 范围,因为它只会带来麻烦(例如,它没有与您的最终工件打包在一起)。
-
不要玩version ranges,除非出于特殊原因并注意指定的版本是默认的最低要求,
[<version>] 表达式是最强的,但你很少需要它.
-
使用 Maven
property 作为库中 version 元素的占位符,以确保您有一个集中位置来对一组具有相同版本值的依赖项进行版本控制.一个经典示例是用于多个依赖项的 spring.version 或 hibernate.version 属性。同样,集中化意味着更好的治理和维护,这也意味着更少的麻烦和更少的地狱。
-
提供时,import 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>
看看这个插件提供的其他standard rules:如果出现错误情况,许多可能有助于破坏构建:
同样,一个常见的parent pom 可以包含多个这些机制(dependencyManagement、强制插件、依赖系列的属性),并确保遵守某些规则。您可能无法涵盖所有可能的场景,但它肯定会降低您感知和体验的地狱程度。