• Home
  • LLMs
  • Python
  • Docker
  • Kubernetes
  • Java
  • Maven
  • All
  • About
Maven | Project Dependencies
  1. Repositories
  2. Direct Dependencies
  3. Transitive Dependencies
  4. Resolve Transitive Dependencies Conflicts

  1. Repositories
    All dependencies (artifacts and plugins) defined in the pom.xml file of the project are downloaded from remote repositories and saved in a local repository on disk. The remote repositories can be defined in the pom.xml file or in the settings.xml file.

    To add repositories in the settings.xml file, you can do the following:
    <?xml version="1.0" encoding="UTF-8" ?>
    <settings>
      <profiles>
        <profile>
          <id>my_profile</id>
          <repositories>
            <repository>
              <id>central</id>
              <name>Maven Central Repository</name>
              <url>https://repo1.maven.org/maven2</url>
              <layout>default</layout>
              <snapshots>
                <enabled>false</enabled>
              </snapshots>
              <releases>
                <enabled>true</enabled>
              </releases>
            </repository>
            <repository>
              <id>internal</id>
              <name>Internal Company Repository</name>
              <url>https://internal.repository.mycompany.com/</url>
              <snapshots>
                <enabled>true</enabled>
              </snapshots>
              <releases>
                <enabled>true</enabled>
              </releases>
            </repository>
          </repositories>
    
          <pluginRepositories>
            <pluginRepository>
              <id>central</id>
              <name>Maven Central Plugin Repository</name>
              <url>https://repo1.maven.org/maven2</url>
              <layout>default</layout>
              <snapshots>
                <enabled>false</enabled>
              </snapshots>
              <releases>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
              </releases>
            </pluginRepository>
            <pluginRepository>
              <id>internal-plugins</id>
              <name>Internal Company Plugin Repository</name>
              <url>https://internal.repository.mycompany.com/</url>
              <snapshots>
                <enabled>true</enabled>
              </snapshots>
              <releases>
                <enabled>true</enabled>
              </releases>
            </pluginRepository>
          </pluginRepositories>
        </profile>
      </profiles>
    
      <activeProfiles>
        <activeProfile>my_profile</activeProfile>
      </activeProfiles>
    </settings>
    Note: The local repository is typically located at "${user.home}/.m2/repository" on Unix-like systems (Linux/macOS) and "%USERPROFILE%\.m2\repository" on Windows.

    In some cases, you might want to add manually an artifact to your local repository. To do so, you can use the command mvn install:install-file:
    mvn install:install-file \
      -DgroupId=mtitek.maven.samples \
      -DartifactId=mtitek-maven-samples \
      -Dversion=1.0.0-SNAPSHOT \
      -Dpackaging=jar \
      -DgeneratePom=true \
      -Dfile=/opt/artifacts/mtitek-maven-samples.jar
    Alternative: You can also install from a local pom.xml file if available:
    mvn install:install-file \
      -DpomFile=path/to/pom.xml \
      -Dfile=/opt/artifacts/mtitek-maven-samples.jar
  2. Direct Dependencies
    There are several important phases in the Maven default lifecycle that are relevant to dependency resolution:
    • First, the compilation of the source code: this phase is referred to as "compile".
    • Second, the compilation of the test source code: this phase is referred to as "test-compile".
    • Third, the execution of tests: this phase is referred to as "test".
    • Fourth, the packaging of the project: this phase is referred to as "package".

    Important notes about phase execution:
    • When the command "mvn compile" is run, only the phases up to and including "compile" will be executed.
    • When the command "mvn test-compile" is run, the phases "compile" and "test-compile" will be executed.
    • When the command "mvn test" is run, the phases "compile", "test-compile", and "test" will be executed.
    • When the command "mvn package" is run, all phases up to and including "package" will be executed (including "compile", "test-compile", "test", and "package").

    We will focus on these phases to explain how Maven resolves project dependencies.
    See this introduction about Maven build phases: http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

    So how does a project dependency affect the build of a project?

    When a dependency is added to the "pom.xml" file, its scope is specified.
    Here's an example of a dependency (the project depends on "junit" to execute test code):
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    Before Maven starts building a project, Maven will try to resolve all project dependencies.
    For each of the build phases, Maven will decide if a dependency is needed to successfully execute that phase.
    The scope of each dependency determines when it is available and needed during the build lifecycle.

    Here are the main scopes of a dependency:

    • compile:
      This is the default scope if you don't specify a scope for the dependency in the pom.xml file.
      Dependencies that have the scope "compile":
      • are available in all classpaths and are needed during compilation, testing, and runtime.
      • will be packaged with the artifact of the project (included in the final JAR/WAR).
      • are transitive (their dependencies are also included).

    • test:
      Dependencies that have the scope "test":
      • are only available during test compilation and execution phases.
      • will not be packaged with the artifact of the project.
      • are not transitive.

    • provided:
      Dependencies that have the scope "provided":
      • are available during compilation and testing but are expected to be provided by the runtime environment (e.g., servlet API provided by application server).
      • will not be packaged with the artifact of the project.
      • are not transitive.

    • runtime:
      Dependencies that have the scope "runtime":
      • are not needed for compilation but are required at runtime and during testing.
      • will be packaged with the artifact of the project.
      • are transitive.

    There are also the scopes "system" and "import". See this page for more details: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope

    Maven provides options to skip compiling, building, and/or executing the test/source code:
    • The parameter "-Dmaven.test.skip=true" will skip compiling, building, and executing the test code.
    • The parameter "-DskipTests" will compile and build the test code but will skip executing the test code.
    • The parameter "-Dmaven.main.skip=true" will skip compiling and packaging the main source code. Note that adding this parameter might cause test compilation to fail if the test code depends on the main source code.

    Here's a table that summarizes the relationship between dependency scope and build phases.
    Let's say a project "A" depends on project "B".
    Scope:
    Direct Dependency
    ("A" ► "B")
    Available During Phases Comment
    compile "compile"
    "test-compile"
    "test"
    "package"
    Available in all phases. If any of these phases needs to be executed for project "A", the build will fail if Maven cannot find the dependency "B" in the repository. The dependency will be included in the final packaged artifact.
    test "test-compile"
    "test"
    If the phases "test-compile" or "test" have to be executed for the project "A", the build will fail if Maven cannot find the dependency "B" in the repository.

    Note that if the phase "compile" has to be executed for the project "A", the build will fail if the code (not test code) of the project "A" refers the code of the project "B".
    provided "compile"
    "test-compile"
    "test"
    If any of the phases "compile", "test-compile", or "test" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "B" in the repository.
    runtime "test"
    "package"
    If any of the phases "test" or "package" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "B" in the repository.

    Note that if the phase "compile" has to be executed for the project "A", the build will fail if the code (not test code) of the project "A" refers the code of the project "B".
  3. Transitive Dependencies
    A transitive dependency defines the relationship between two projects that are not linked together by a direct dependency.

    For example, let's say project "A" has a direct dependency on project "B" and project "B" has a direct dependency on project "C". Assuming that project "A" has no direct dependency on project "C", then project "A" has a transitive dependency on project "C".

    In other words: if "A" depends on "B" ("A" ► "B") and "B" depends on "C" ("B" ► "C") then "A" depends on "C" ("A" ► "C").

    That said, we need to know when and how Maven resolves transitive dependencies.
    The scope of the direct and transitive dependencies will let Maven decide if they are needed to successfully execute a specific phase.

    For the following, let's suppose that:
    • project "A" depends on project "B" and project "B" depends on project "C".
    • project "A" has no direct dependency on project "C".
    • project "A" might have a transitive dependency on project "C".

    First we need to define the scope of the direct dependency of project "A" on project "B".
    And second we need to define the scope of the direct dependency of project "B" on project "C".
    Then we can resolve the scope of the transitive dependency of project "A" on project "C".

    Once the scope of the transitive dependency is known, Maven will use the same rules to decide if the project referred by the transitive dependency is needed to successfully execute a build phase.

    The transitive dependency scope resolution follows these rules:

    • compile
      If the scope of the direct dependency of "B" on "C" is "compile", then the scope of the transitive dependency of "A" on "C" is the same scope as the direct dependency of "A" on "B".

      Here's a table that summarizes the case when the scope of the direct dependency of "B" on "C" is "compile":

      Scope:
      Direct Dependency
      ("B" ► "C")
      Scope:
      Direct Dependency
      ("A" ► "B")
      Scope:
      Transitive Dependency
      ("A" ► "C")
      Available During Phases Comment
      compile compile compile "compile"
      "test-compile"
      "package"
      If any of the phases "compile", "test-compile", or "package" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "C" in the repository.
      compile test test "test-compile" If the phase "test-compile" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "C" in the repository.

      Note that if the phase "compile" has to be executed for the project "A", the build will fail if the code (not test code) of the project "A" refers to the code of the project "C".
      compile provided provided "compile"
      "test-compile"
      If any of the phases "compile" or "test-compile" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "C" in the repository.
      compile runtime runtime "test-compile"
      "package"
      If any of the phases "test-compile" or "package" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "C" in the repository.

      Note that if the phase "compile" has to be executed for the project "A", the build will fail if the code (not test code) of the project "A" refers to the code of the project "C".

    • runtime
      If the scope of the direct dependency of "B" on "C" is "runtime", then the scope of the transitive dependency of "A" on "C" follows these rules:
      • If the direct dependency of "A" on "B" is "compile", then the transitive scope is "runtime".
      • If the direct dependency of "A" on "B" is "test", then the transitive scope is "test".
      • If the direct dependency of "A" on "B" is "provided", then the transitive scope is "provided".
      • If the direct dependency of "A" on "B" is "runtime", then the transitive scope is "runtime".

      Here's a table that summarizes the case when the scope of the direct dependency of "B" on "C" is "runtime":

      Scope:
      Direct Dependency
      ("B" ► "C")
      Scope:
      Direct Dependency
      ("A" ► "B")
      Scope:
      Transitive Dependency
      ("A" ► "C")
      Available During Phases Comment
      runtime compile runtime "test-compile"
      "package"
      If any of the phases "test-compile" or "package" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "C" in the repository.

      Note that if the phase "compile" has to be executed for the project "A", the build will fail if the code (not test code) of the project "A" refers to the code of the project "C".
      runtime test test "test-compile" If the phase "test-compile" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "C" in the repository.

      Note that if the phase "compile" has to be executed for the project "A", the build will fail if the code (not test code) of the project "A" refers to the code of the project "C".
      runtime provided provided "compile"
      "test-compile"
      If any of the phases "compile" or "test-compile" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "C" in the repository.
      runtime runtime runtime "test-compile"
      "package"
      If any of the phases "test-compile" or "package" has to be executed for the project "A", the build will fail if Maven cannot find the dependency "C" in the repository.

      Note that if the phase "compile" has to be executed for the project "A", the build will fail if the code (not test code) of the project "A" refers to the code of the project "C".

    Note that if the scope of the direct dependency of "B" on "C" is "provided" or "test", then "C" is not visible for "A". Maven will throw an error if the code (including test code) of "A" refers to the code of "C".
  4. Resolve Transitive Dependencies Conflicts
    Transitive dependencies might bring different versions of the same artifacts. Maven needs to pick only one version of the artifacts for your project. The closest version (regardless if it's the newest or not) in the dependencies tree will be chosen. If there are multiple versions on the same level of tree, then Maven will choose the first one found.

    In many cases, the version chosen by Maven might not be the right one for your project. To address this issue, you need to add the artifact as a dependency to the pom.xml file of your project and specify the version you want to use.

    In some cases you might want to exclude a transitive dependency so it (and all its direct and transitive dependencies) won't be resolved by Maven:
    <dependency>
      <groupId>A</groupId>
      <artifactId>A</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <exclusions>
        <exclusion>
          <groupId>B</groupId>
          <artifactId>B</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    You can use the Maven Dependency Plugin to print the dependency tree of your project:
    $ mvn dependency:tree
© 2025  mtitek