Maven sourcing dependency version from great

Below shows the hierarchy of my POMs.

You can see that we have a company parent-pom for spring boot projects. This POM has spring-boot-starter as it's parent, and it imports our own dependency-management BOM.

[INFO] --- hierarchy-maven-plugin:1.4:tree (default-cli) @ user-service ---
[INFO]  PARENT com.MY_COMPANY.platform:user:3.20.14-SNAPSHOT
[INFO]    PARENT com.MY_COMPANY.platform:spring-boot-parent:3.20.12-SNAPSHOT
[INFO]      PARENT org.springframework.boot:spring-boot-starter-parent:1.5.12.RELEASE
[INFO]        PARENT org.springframework.boot:spring-boot-dependencies:1.5.12.RELEASE  <<<< This pom defines assertJ 2.x
[INFO]          [ other imports ]
[INFO]      IMPORT com.MY_COMPANY:dependencyManagementBase:2.23.14-SNAPSHOT     <<<<<<<<<<<< This pom defines assertJ 3.x
[INFO]    IMPORT com.MY_COMPANY.platform:platform-dependency-management:1.20.7
[INFO] ------------------------------------------------------------------------

To focus on a specific, we define AssertJ 3 in our dependency-management; however, spring-boot-dependencies defines AssertJ 2. Not a big deal with assertJ, but there are other fish like Mongo-Java-Driver that are not picking up our version.

How does Maven choose precedence here? Why doesn't our dependency-management win over a far-ancestor's dependency-management?

I also noticed that if I add AssertJ as a dependency of MY_COMPANY.platform:spring-boot-parent, it also does NOT use the version in our dependency-management (so I'll just keep it there for now, so the hierarchy under microscope is shorter).

EDIT - Add abbreviated POMs

com.MY_COMPANY.platform:spring-boot-parent

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.MYCOMPANY.platform</groupId>
    <artifactId>spring-boot-parent</artifactId>
    <version>3.20.12-SNAPSHOT</version>
    <packaging>pom</packaging>

    <prerequisites>
        <maven>3.0.4</maven>
    </prerequisites>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.12.RELEASE</version>
    </parent>

    <properties>
        <MYCOMPANYdependencymanagement.version>2.23.13</MYCOMPANYdependencymanagement.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.MYCOMPANY</groupId>
                <artifactId>dependencyManagementBase</artifactId>
                <version>${MYCOMPANYdependencymanagement.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

com.MY_COMPANY:dependencyManagementBase

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.MYCOMPANY</groupId>
    <artifactId>dependencyManagementBase</artifactId>
    <version>2.23.13</version>
    <packaging>pom</packaging>

    <modules>
        <module>spring-dep-man</module>
    </modules>

    <properties>
        <org.assertj-core.version>3.5.2</org.assertj-core.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.assertj</groupId>
                <artifactId>assertj-core</artifactId>
                <version>${org.assertj-core.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

**EDIT 2 - Add verbose hierarchy showing different versions **

~/p/springbootparentpom> mvn hierarchy:tree -Dlevel=full
[INFO] --- hierarchy-maven-plugin:1.4:tree (default-cli) @ spring-boot-parent ---
[INFO] Displaying hierarchy.
[INFO]  PARENT org.springframework.boot:spring-boot-starter-parent:1.5.12.RELEASE
[INFO]    PARENT org.springframework.boot:spring-boot-dependencies:1.5.12.RELEASE
[INFO]          DEP_MANAGEMENT ........
[INFO]          DEP_MANAGEMENT org.assertj:assertj-core:2.6.0
[INFO]          [ ... Many DEP_MAN and IMPORT ... ]
[INFO]  IMPORT com.MYCOMPANY:dependencyManagementBase:2.23.14-SNAPSHOT
[INFO]        DEP_MANAGEMENT ........
[INFO]        DEP_MANAGEMENT org.assertj:assertj-core:3.5.2
[INFO]        DEP_MANAGEMENT ........

My guess is that it is grabbing the nearest.

Dependency mediation - this determines what version of a dependency will be used when multiple versions of an artifact are encountered. Currently, Maven 2.0 only supports using the "nearest definition" which means that it will use the version of the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it explicitly in your project's POM. Note that if two dependency versions are at the same depth in the dependency tree, until Maven 2.0.8 it was not defined which one would win, but since Maven 2.0.9 it's the order in the declaration that counts: the first declaration wins.

What I do for stuff like this is go to my IDE's dependencies section for a given project. It shows me all dependencies, what version, and where it came from (see bottom of pop-up on attached image below for an example).

在这里输入图像描述


The issue is that your project is relying not on a parent for dependency management, but on an imported project. The Maven dependency hierarchy doesn't work that way. Probably the best solution would be to move the 'parent spring-boot-starter-parent' declaration into the MY_COMPANY:dependencyManagementBase project. Then change the parent declaration in com.MYCOMPANY.platform to point at your dependencyManagementBase project. That way you would have a clear inheritance hierarchy.

What you currently have does not really use dependency management. ie if you were to change the 'dependecyManagement' section in the dependencyManagementBase project to 'dependency', you would get the same results.


Doesn't seem like anyone has the answer. So I'll just answer with my findings.

Given OP doesn't work. My next strategy is to IMPORT the spring-boot-starter-parent and spring-boot-dependency POMs, since I can't use them as parents.

This however has it's own behaviors.

I found that if my dependency management looks like this, my versions win:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.mycompany</groupId>
            <artifactId>dependency-management-slim</artifactId>
            <version>2.23.14-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

However, I decided I wanted to separate my custom code from my spring-boot mimicking pom. So in order to preserve priority I figured I had to do this:

service-root -> only import my-dependency-management
^-- spring-boot-parent -> mimick and import spring-boot-starter-parent
^-- service-parent -> has our common dependencies and profiles
^-- service-impl - code

However, that did NOT work; spring-boot versions overrode our custom versions. So I had to do this:

root-parent -> nothing
^-- spring-boot-parent -> mimick and import spring-boot-starter-parent
^-- service-parent -> import my-dependency-management
^-- service-impl - code

Conclusions

  • IF importing multiple dependency-management in the SAME pom, then the first management-versions win.
  • IF you are importing in multiple poms, the last one imported wins.
  • IF a parent defines has dependency-management with individual dependencies defined, you cannot override them with dependency management (This is original problem).
  • ¯_(ツ)_/¯

    链接地址: http://www.djcxy.com/p/29694.html

    上一篇: 运行mvn依赖关系时,Maven无法识别兄弟模块:树

    下一篇: 从伟大的Maven采购依赖版本