Why order of Maven dependencies matter?

I thought that the order of Maven dependencies doesn't matter before and regard this as a pro of it. And this is my old pom.xml's dependencies:

<dependencies>

    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>2.19</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-spring3</artifactId>
        <version>2.19</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-moxy</artifactId>
        <version>2.19</version>
    </dependency>

</dependencies>

It works well, and today I wanna move spring dependency to the bottom so that those jersey related can be together. However then I can no longer make it working, my Jetty complains:

[ERROR] Failed to execute goal org.eclipse.jetty:jetty-maven-plugin:9.3.0.M1:run (default-cli) on project mtest: Execution default-cli of goal org.eclipse.jetty:jetty-maven-plugin:9.3.0.M1:run failed: A required class was missing while executing org.eclipse.jetty:jetty-maven-plugin:9.3.0.M1:run: org/apache/commons/logging/LogFactory

That is really confusing, so do I have to concern about dependencies order? How do I know the correct order?


Solution 1:

The order of dependencies does matter because of how Maven resolves transitive dependencies, starting with version 2.0.9. Excerpt from the documentation:

(...) this determines what version of a dependency will be used when multiple versions of an artifact are encountered. (...) You can always guarantee a version by declaring it explicitly in your project's POM. (...) since Maven 2.0.9 it's the order in the declaration that counts: the first declaration wins.

Solution 2:

To expand upon the other answer (which states that the declaration order affects Maven's dependency mediation for transitive dependencies), there are a few tools you can use:

  • mvn dependency:tree [-Dscope=[runtime|test]] will show you what dependencies will be available for the selected scope. See here for details
  • mvn dependency:build-classpath gives you order in which dependencies are available on your classpath (if two or more classpath entries have the same class, the earlier one wins). See here for details

I don't know much about your situation, but it's often the case that you wind up with the wrong version of 1 or more jars at compile/runtime. Declaring your own version of the library in question or locking down the version with <dependencyManagement> are options here.

Now to answer your other question - how do you know what the right order is when declaring dependencies?

My suggestion - the right declaration order is the one that gets you the versions of the dependencies you want, in the order you want them in. Use the tools above to check your dependencies, and tweak the declared order if necessary.

Note that most jars contain disjointedly-named classes, so the exact order in which jars appear on your classpath is usually not that important. The only exception I've noticed is some jars in SLF4J which intentionally shadow classes from the other logger libraries it's intended to replace.