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