Direct Dependency & Transitive Dependency

There’re two types of dependencies in Maven:

  • direct
  • transitive

Direct dependencies are the ones that are explicitly included in the project. These can be included in the project using <dependency> tags:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

Transitive dependencies, on the other hand, are dependencies required by our direct dependencies. Required transitive dependencies are automatically included in our project by Maven.

We can list all dependencies including transitive dependencies in the project using:

mvn dependency:tree

Dependency Scopes

the scope element refers to the classpath of the task at hand (compiling and runtime, testing, etc.) as well as how to limit the transitivity of a dependency

maven has 6 default dependency scopes

  • default scope when no other scope is provided
  • dependencies with this scope are available on all classpaths
  • dependencies with this scope are transitive
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
</dependency>
  • used to mark dependencies that should be provided at runtime by JDK or a container
  • dependencies with this scope are available only at compile and test classpaths (not runtime)
  • dependencies with this scope are NOT transitive

A good use case for this scope would be a web application deployed in some container, where the container already provides some libraries itself.

For example, a web server that already provides the Servlet API at runtime, thus in our project, those dependencies can be defined with the provided scope

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
</dependency>
  • indicates that the dependency is not required for compilation, but is for execution
  • dependencies with this scope are available only at runtime and test classpaths (not compile)

A good example of dependencies that should use the runtime scope is a JDBC driver

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.6</version>
    <scope>runtime</scope>
</dependency>
  • indicates that the dependency is not required at standard runtime of the application, but is used only for test purposes
  • dependencies with this scope are only available for test-compilation and test-execution classpaths
  • dependencies with this scope are NOT transitive

The standard use case for this scope is adding test library like JUnit to our application:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
  • system scope is much similar to the provided scope
  • the main difference between those two scopes is that system requires us to directly point to specific jar on the system

The important thing to remember is that building the project with system scope dependencies may fail on different machines if dependencies aren’t present or are located in a different place than the one systemPath points to:

<dependency>
    <groupId>com.marcuschiu</groupId>
    <artifactId>custom-dependency</artifactId>
    <version>1.3.2</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/libs/custom-dependency-1.3.2.jar</systemPath>
</dependency>
  • This scope was added in Maven 2.0.9 and it’s only available for the dependency type pom
  • Import indicates that this dependency should be replaced with all effective dependencies declared in it’s POM
<dependency>
    <groupId>com.marcuschiu</groupId>
    <artifactId>custom-project</artifactId>
    <version>1.3.2</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

Scope & Transitivity

a project’s dependency that is marked:

  • compile scope, then:
    • all dependencies with runtime scope will be pulled in with runtime scope in the project
    • all dependencies with compile scope will be pulled in with compile scope in the project
  • provided scope, then both runtime and compile scope dependencies will be pulled in with the provided scope in the project
  • test scope, then both runtime and compile scope transitive dependencies will be pulled in with the test scope in the project
  • runtime scope, then both runtime and compile scope transitive dependencies will be pulled in with the runtime scope in the project

NOTE: a project with dependencies that contains dependencies with scopes provided and test will never be included in the project