【问题标题】:Maven: Different library versions in one JVMMaven:一个 JVM 中的不同库版本
【发布时间】:2013-11-28 04:37:08
【问题描述】:

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

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

一个简化的例子可以是:

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

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

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

我的问题是:

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

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

【问题讨论】:

  • “最终的 jar 文件同时包含 CommonModule-v1.jarCommonModule-v2.jar”是什么意思?您通常不会将 JAR 打包到其他 JAR 中。您的问题对于 Java 应用程序的通常部署方式并没有多大意义。 (在类路径的某处有一个所有传递依赖项的平面列表。)
  • Maven会使用最新版本,你可以试试dependency:analyze-duplicate。
  • @millimoose,欢迎来到 maven
  • @user2250246 我对 Maven 非常了解,而您所描述的并不是 Maven 的工作方式。对于一个mvn package 只会对给定模块的类文件进行JAR,编译后它不会对依赖项做任何事情。如果您正在制作 WAR,那么它会将依赖项放入 WEB-INF/lib/,但在这种情况下,它将选择其中一个版本,而不是两个版本。 (除非不同的版本有不同的groupId+artifactId,这不是 Maven 可以自动处理的。)那么,不如你描述实际发生的事情而不是油嘴滑舌呢?
  • tl;dr:默认情况下,Maven 不会将 jars 放入其他 jars 中。如果有,请发布相关配置。

标签: java maven


【解决方案1】:

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

此规则要求依赖版本号收敛。如果一个 项目有两个依赖,A和B,都依赖于同一个 工件,C,如果 A 依赖于不同的 C 的版本,那么 C 的版本依赖于 B。

<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>

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

【讨论】:

    【解决方案2】:

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

    以下是您如何排除的示例:

        <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 中。

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

      • Maven 只会在构建中找到两次 JAR 时自动选择 一个 版本。但是,&lt;dependencyManagement&gt; 是决定哪个版本的方法。
      • 他说将模块打包在一起。这些模块可能已经打包。但这只是一个疯狂的猜测。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-10-28
            • 1970-01-01
            • 2016-09-18
            • 1970-01-01
            • 2023-04-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多