Maven: The Complete Reference - 3.4. Project Dependencies |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
Maven can manage both internal and external dependencies. An external dependency for a Java project might be a library such as Plexus, the Spring Framework, or Log4J. An internal dependency is illustrated by a web application project depending on another project that contains service classes, model objects, or persistence logic. Project Dependencies shows some examples of project dependencies. <project> ... <dependencies> <dependency> <groupId>org.codehaus.xfire</groupId> <artifactId>xfire-java5</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4</version> <scope>provided</scope> </dependency> </dependencies> ... </project>
The first dependency is a compile dependency on the XFire SOAP library
from Codehaus. You would use this type of dependency if your project
depended on this library for compilation, testing, and during
execution. The second dependency is a Project Dependencies briefly introduced three of the five dependency
scopes:
Assume that you are working on a library that provides caching behavior. Instead of writing a caching system from scratch, you want to use some of the existing libraries that provide caching on the file system and distributed caches. Also assume that you want to give the end user an option to cache on the file system or to use an in-memory distributed cache. To cache on the file system, you’ll want to use a freely available library called EHCache (http://ehcache.sourceforge.net/), and to cache in a distributed in-memory cache, you want to use another freely available caching library named SwarmCache ( http://swarmcache.sourceforge.net/ ). You’ll code an interface and create a library that can be configured to use either EHCache or SwarmCache, but you want to avoid adding a dependency on both caching libraries to any project that depends on your library. In other words, you need both libraries to compile this library project, but you don’t want both libraries to show up as transitive runtime dependencies for the project that uses your library. You can accomplish this by using optional dependencies as shown in Declaring Optional Dependencies. Declaring Optional Dependencies. <project> <modelVersion>4.0.0</modelVersion> <groupId>org.sonatype.mavenbook</groupId> <artifactId>my-project</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>1.4.1</version> <optional>true</optional> </dependency> <dependency> <groupId>swarmcache</groupId> <artifactId>swarmcache</artifactId> <version>1.0RC2</version> <optional>true</optional> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.13</version> </dependency> </dependencies> </project>
Since you’ve declared these dependencies as optional in <project> <modelVersion>4.0.0</modelVersion> <groupId>org.sonatype.mavenbook</groupId> <artifactId>my-application</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>org.sonatype.mavenbook</groupId> <artifactId>my-project</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>1.4.1</version> </dependency> </dependencies> </project> In an ideal world, you wouldn’t have to use optional
dependencies. Instead of having one large project with a series of
optional dependencies, you would separate the EHCache-specific code to
a Instead of a specific version for each dependency, you can alternatively specify a range of versions that would satisfy a given dependency. For example, you can specify that your project depends on version 3.8 or greater of JUnit, or anything between versions 4.5 and 4.10 of JUnit. You do this by surrounding one or more version numbers with the following characters:
For example, if you wished to access any Specifying a Dependency Range: JUnit 3.8 - JUnit 4.0. <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>[3.8,4.0)</version> <scope>test</scope> </dependency>
If you want to depend on any version of JUnit no higher than 3.8.1, you would specify only an upper inclusive boundary, as shown in Specifying a Dependency Range: JUnit ⇐ 3.8.1. Specifying a Dependency Range: JUnit ⇐ 3.8.1. <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>[,3.8.1]</version> <scope>test</scope> </dependency>
A version before or after the comma is not required, and means +/- infinity. For example, "[4.0,)" means any version greater than or equal to 4.0. "(,2.0)" is any version less than 2.0. "[1.2]" means only version 1.2, and nothing else. NoteWhen declaring a "normal" version such as 3.8.2 for Junit,
internally this is represented as "allow anything, but prefer 3.8.2."
This means that when a conflict is detected, Maven is allowed to use
the conflict algorithms to choose the best version. If you specify
[3.8.2], it means that only 3.8.2 will be used and nothing else. If
somewhere else there is a dependency that specifies [3.8.1], you would
get a build failure telling you of the conflict. We point this out to
make you aware of the option, but use it sparingly and only when
really needed. The preferred way to resolve this is via
Maven accomplishes this by building a graph of dependencies and
dealing with any conflicts and overlaps that might occur. For example,
if Maven sees that two projects depend on the same Each of the scopes outlined earlier in the section Section 3.4.1, “Dependency Scope” affects not just the scope of the dependency in the declaring project, but also how it acts as a transitive dependency. The easiest way to convey this information is through a table, as in Table 3.1, “How Scope Affects Transitive Dependencies”. Scopes in the top row represent the scope of a transitive dependency. Scopes in the leftmost column represent the scope of a direct dependency. The intersection of the row and column is the scope that is assigned to a transitive dependency. A blank cell in this table means that the transitive dependency will be omitted. Table 3.1. How Scope Affects Transitive Dependencies
To illustrate the relationship of transitive dependency scope to
direct dependency scope, consider the following example. If
You can think of this as a transitive boundary which acts as a filter on dependency scope. Transitive dependencies which are provided and test scope usually do not affect a project. Transitive dependencies which are compile and runtime scoped usually affect a project regardless of the scope of a direct dependency. Transitive dependencies which are compile scoped will have the same scope of the direct dependency . Transitive dependencies which are runtime scoped will generally have the same scope of the direct dependency except when the direct dependency has a scope of compile. When a transitive dependency is runtime scoped and the direct dependency is compile scoped, the transitive dependency will have an effective scope of runtime. There will be times when you need to exclude a transitive dependency,
such as when you are depending on a project that depends on another
project, but you would like to either exclude the dependency
altogether or replace the transitive dependency with another
dependency that provides the same functionality. Excluding a Transitive Dependency shows
an example of a dependency element that adds a dependency on
Excluding a Transitive Dependency. <dependency> <groupId>org.sonatype.mavenbook</groupId> <artifactId>project-a</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>org.sonatype.mavenbook</groupId> <artifactId>project-b</artifactId> </exclusion> </exclusions> </dependency>
Often, you will want to replace a transitive dependency with another implementation. For example, if you are depending on a library that depends on the Sun JTA API, you may want to replace the declared transitive dependency. Hibernate is one example. Hibernate depends on the Sun JTA API JAR, which is not available in the central Maven repository because it cannot be freely redistributed. Fortunately, the Apache Geronimo project has created an independent implementation of this library that can be freely redistributed. To replace a transitive dependency with another dependency, you would exclude the transitive dependency and declare a dependency on the project you wanted instead. Excluding and Replacing a Transitive Dependency shows an example of a such replacement. Excluding and Replacing a Transitive Dependency. <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>3.2.5.ga</version> <exclusions> <exclusion> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-jta_1.1_spec</artifactId> <version>1.1</version> </dependency> </dependencies>
In Excluding and Replacing a Transitive Dependency, there is nothing marking the dependency on geronimo-jta_1.1_spec as a replacement, it just happens to be a library which provides the same API as the original JTA dependency. Here are some other reasons you might want to exclude or replace transitive dependencies:
Once you’ve adopted Maven at your super complex enterprise and you
have two hundred and twenty inter-related Maven projects, you are
going to start wondering if there is a better way to get a handle on
dependency versions. If every single project that uses a dependency
like the MySQL Java connector needs to independently list the version
number of the dependency, you are going to run into problems when you
need to upgrade to a new version. Because the version numbers are
distributed throughout your project tree, you are going to have to
manually edit each of the pom.xml files that reference a dependency
to make sure that you are changing the version number everywhere. Even
with Luckily, Maven provides a way for you to consolidate dependency
version numbers in the For example, if you have a large set of projects which make use of the
MySQL Java connector version 5.1.2, you could define the following
Defining Dependency Versions in a Top-level POM. <project> <modelVersion>4.0.0</modelVersion> <groupId>org.sonatype.mavenbook</groupId> <artifactId>a-parent</artifactId> <version>1.0.0</version> ... <dependencyManagement> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.2</version> <scope>runtime</scope> </dependency> ... <dependencies> </dependencyManagement>
Then, in a child project, you can add a dependency to the MySQL Java Connector using the following dependency XML: <project> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.sonatype.mavenbook</groupId> <artifactId>a-parent</artifactId> <version>1.0.0</version> </parent> <artifactId>project-a</artifactId> ... <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> </project> You should notice that the child project did not have to explicitly
list the version of the Dependency management in a top-level POM is different from just
defining a dependency on a widely shared parent POM. For starters, all
dependencies are inherited. If |