One of the compelling reasons to use Maven is that it makes the
process of tracking down dependencies (and dependencies of
dependencies) very easy. When a project depends on an artifact
produced by another project we say that this artifact is a
dependency. In the case of a Java project, this can be as simple as a
project depending on an external dependency like Log4J or JUnit. While
dependencies can model external dependencies, they can also manage the
dependencies between a set of related projects. If project-a
depends
on project-b
, Maven is smart enough to know that project-b
must be
built before project-a
.
Relationships are not only about dependencies and figuring out what
one project needs to be able to build an artifact. Maven can model the
relationship of a project to a parent, and the relationship of a
project to submodules. This section gives an overview of the various
relationships between projects and how such relationships are
configured.
3.5.1. More on Coordinates
Coordinates define a unique location for a project. Projects are
related to one another using Maven Coordinates. project-a
doesn’t
just depend on project-b
; a project with a groupId
, artifactId
,
and version
depends on another project with a groupId
,
artifactId
, and version
. To review, a Maven Coordinate is made up
of three components:
-
groupId
-
A
groupId
groups a set of related artifacts. Group identifiers
generally resemble a Java package name. For example, the groupId
org.apache.maven
is the base groupId for all artifacts produced by
the Apache Maven project. Group identifiers are translated into
paths in the Maven Repository; for example, the org.apache.maven
groupId can be found in /maven2/org/apache/maven on
repo1.maven.org.
-
artifactId
-
The
artifactId
is the project’s main identifier. When you generate
an artifact, this artifact is going to be named with the
artifactId
. When you refer to a project, you are going to refer to
it using the artifactId
. The artifactId
, groupId
combination
must be unique. In other words, you can’t have two separate projects
with the same artifactId
and groupId
; artifactId
s are unique
within a particular groupId
.
Note
While '.'s are commonly used in groupId
s, you should try to
avoid using them in artifactId
s. This can cause issues when trying
to parse a fully qualified name down into the subcomponents.
-
version
-
When an artifact is released, it is released with a version
number. This version number is a numeric identifier such as "1.0",
"1.1.1", or "1.1.2-alpha-01". You can also use what is known as a
snapshot version. A snapshot version is a version for a component
which is under development, snapshot version numbers always end in
SNAPSHOT; for example, "1.0-SNAPSHOT", "1.1.1-SNAPSHOT", and
"1-SNAPSHOT". the section called “Version Build Numbers”
introduces versions and version ranges.
There is a fourth, less-used qualifier:
-
classifier
-
You would use a classifier if you were releasing the same code but
needed to produce two separate artifacts for technical reasons. For
example, if you wanted to build two separate artifacts of a JAR, one
compiled with the Java 1.4 compiler and another compiled with the
Java 6 compiler, you might use the classifier to produce two
separate JAR artifacts under the same groupId:artifactId:version
combination. If your project uses native extensions, you might use
the classifier to produce an artifact for each target
platform. Classifiers are commonly used to package up an artifact’s
sources, JavaDocs or binary assemblies.
When we talk of dependencies in this book, we often use the following
shorthand notation to describe a dependency:
groupId:artifactId:version
. To refer to the 2.5 release of the
Spring Framework, we would refer to it as
org.springframework:spring:2.5
. When you ask Maven to print out a
list of dependencies with the Maven Dependency plugin, you will also
see that Maven tends to print out log messages with this shorthand
dependency notation.
3.5.2. Project Inheritance
There are going to be times when you want a project to inherit values
from a parent POM. You might be building a large system, and you don’t
want to have to repeat the same dependency elements over and over
again. You can avoid repeating yourself if your projects make use of
inheritance via the parent element. When a project specifies a parent,
it inherits the information in the parent project’s POM. It can then
override and add to the values specified in this parent POM.
All Maven POMs inherit values from a parent POM. If a POM does not
specify a direct parent using the parent
element, that POM will
inherit values from the Super POM. Project Inheritance shows the
parent
element of project-a
which inherits the POM defined by the
a-parent
project.
Project Inheritance.
<project>
<parent>
<groupId>com.training.killerapp</groupId>
<artifactId>a-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>project-a</artifactId>
...
</project>
Running mvn help:effective-pom
in project-a
would show a POM that
is the result of merging the Super POM with the POM defined by
a-parent
and the POM defined in project-a
. The implicit and
explicit inheritance relationships for project-a
are shown in
Figure 3.3, “Project Inheritance for a-parent and project-a”.
When a project specifies a parent project, Maven uses that parent POM
as a starting point before it reads the current project’s POM. It
inherits everything, including the groupId
and version
number. You’ll notice that project-a
does not specify either, both
groupId
and version
are inherited from a-parent
. With a parent
element, all a POM really needs to define is an artifactId
. This
isn’t mandatory, project-a
could have a different groupId
and
version
, but by not providing values, Maven will use the values
specified in the parent POM. If you start using Maven to manage and
build large multi-module projects, you will often be creating many
projects which share a common groupId
and version
.
When you inherit a POM, you can choose to live with the inherited POM
information or to selectively override it. The following is a list of
items a Maven POM inherits from its parent POM:
-
identifiers (at least one of
groupId
or artifactId
must be
overridden.)
-
dependencies
-
developers and contributors
-
plugin lists
-
reports lists
-
plugin executions (executions with matching ids are merged)
-
plugin configuration
When Maven inherits dependencies, it will add dependencies of child
projects to the dependencies defined in parent projects. You can use
this feature of Maven to specify widely used dependencies across all
projects which inherit from a top-level POM. For example, if your
system makes universal use of the Log4J logging framework, you can
list this dependency in your top-level POM. Any projects which inherit
POM information from this project will automatically have Log4J as a
dependency. Similarly, if you need to make sure that every project is
using the same version of a Maven plugin, you can list this Maven
plugin version explicitly in a top-level parent POM’s
pluginManagement
section.
Maven assumes that the parent POM is available from the local
repository, or available in the parent directory (../pom.xml) of the
current project. If neither location is valid this default behavior
may be overridden via the relativePath
element. For example, some
organizations prefer a flat project structure where a parent project’s
pom.xml isn’t in the parent directory of a child project. It might
be in a sibling directory to the project. If your child project were
in a directory ./project-a and the parent project were in a
directory named ./a-parent, you could specify the relative location
of parent-a
's POM with the following configuration:
<project>
<parent>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../a-parent/pom.xml</relativePath>
</parent>
<artifactId>project-a</artifactId>
</project>