4

我有一个项目,它的依赖树很大,即它包含来自多个团队的模块。

现在有一些常用的依赖项在多个模块中是通用的。

一个简化的例子可以是:

TopModule.jar
  ChildModule.jar
    CommonModule-v1.jar
  CommonModule-v2.jar

当我构建我的项目时,我指定了公共依赖项的最新版本,但很难向其他团队提出相同的要求。

因此,TopModule 通常是使用不同版本的 CommonModule(上例中的 v1 和 v2)构建的。

我的问题是:

如果最终的 jar 文件同时包含 CommonModule-v1.jar 和 CommonModule-v2.jar,它对运行时有何影响?

运行时是否会错误地加载需要 v1 的版本 v2,反之亦然?

4

6 回答 6

4

Maven 最终只会使用每个工件的一个版本——它不会做任何花哨的类加载器隔离技巧。您可以看到它将与哪个版本一起使用mvn dependency:resolve

如果需要在依赖项中使用特定版本,可以使用shade 插件。它会进行重命名技巧,以便依赖项获得自己的库版本。

于 2013-11-14T22:52:09.667 回答
1

要在全球范围内解决此问题,请使用此DependencyConvergence Rule

此规则要求依赖版本号收敛。如果一个项目有两个依赖项 A 和 B,它们都依赖于同一个工件 C,如果 A 依赖于不同版本的 C,则此规则将导致构建失败,然后 B 依赖于 C 的版本。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>1.3.1</version>
    <executions>
      <execution>
        <id>enforce</id>
        <configuration>
          <rules>
            <DependencyConvergence/>
          </rules>
        </configuration>
        <goals>
          <goal>enforce</goal>
        </goals>
      </execution>
    </executions>
</plugin>

在此之后,所有团队都使用一致版本的依赖项一起工作。

于 2013-11-14T23:01:05.617 回答
0

除了@yshavit 所说的之外,理想情况下,您应该排除早期版本的,CommonModule以便只有 v2 位于类路径中。这只有在CommonModulev2 api 向后兼容CommonModulev1 时才有可能。

这是您如何排除的示例:

    <dependency>
        <groupId>ChildModuleGroupid</groupId>
        <artifactId>ChildModuleArtifactid</artifactId>
        <version>1.0</version>
        <exclusions>
            <exclusion>
                <groupId>CommonModuleGroupId</groupId>
                <artifactId>CommonModuleArtifactId</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

您可以将它放在 TopModule pom.xml 中。

于 2013-11-14T23:02:34.653 回答
0

如果您的 pom.xml 只是已打包模块的聚合器,那么这适用。如果不是这种情况,并且您的项目实际上将所有这些模块编译并打包为子模块,那么 maven 将选择一个。如果它自己编译每个项目,那么它将使用该依赖项进行打包。但是,如果所有这些最终都在同一个类加载器中,那么它将无法正常工作。

在运行时它可能会导致错误,想想method not found等等。您的字节码类已编译并与正确的依赖项链接,但由于类加载器找到两个候选者,它只在运行时加载一个。

您可以做的是设置一个定义 a 的父 pom <dependencyManagement>,然后要求所有团队将其用作父级,不要声明<version>而是从父级 pom.xml 继承它。

参考资料http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management

于 2013-11-14T22:50:07.647 回答
0

这取决于模块在 maven 中的命名方式。通常,maven 会尝试解决冲突的库并将最高版本放入树中。但是,如果库在 artifactId 方面是不同的工件,那么 maven 将不会看到它们来自同一个品种,因此不会解决歧义。

通常你通过一个共同的 parent.pom 来解决这个问题,你可以在其中定义整个项目中常用库的版本。如果您无法控制其他项目(不是构建的一部分,只有依赖项),您可能很幸运能够让您的最终项目正常工作。如果该库在较新版本中破坏了兼容性,您将无法使用它。

那么,您的最终项目是否包含库的两个版本,您检查了吗?依赖关系树可能显示两个版本,但如果 maven 将仅使用层次结构中依赖关系的最新版本。

于 2013-11-14T22:51:32.067 回答
0

Classloader 将加载出现在您的类路径中的第一个 JAR。更详细地说——它会在你的类路径中搜索第一个类,所以在每种情况下,所有这些搜索都会落入 ieCommonModule-v2.jar中。所以答案是肯定的——如果它出现在你的类路径中,它可能会错误地加载 v1 的版本 v2。

于 2013-11-14T22:52:51.983 回答