If you are a Java developer, one of the most frequent words that you hear during your career is Maven. So, what is Maven, and what does it do? This article gives you a basic idea about what Maven is and the important concepts associated with it.
Although Maven is considered a build tool that can be used to build artifacts from the source code by many, it is actually a project management tool that offers the build tool capabilities as a subset of its capabilities.
As indicated on the official site, https://maven.apache.org,
Apache Maven is a Software project management and comprehension tool. Based on the concept of a project object model(POM), Maven can manage a project’s build, reporting, and documentation from a central piece of information.
Looking at Maven comprehensively,
Having the aforementioned features, Maven can give a simple project setup that uses the best practices, and with Maven, the projects can follow a consistent structure that is independent of the IDEs(Integrated Development Environments).
In addition, one of the most important usages of Maven is that it simplifies the declaration of project dependencies.
Focusing on the last point, when you build a traditional Java application, you include the dependencies by downloading them directly and including them in the project structure. If you have already done this you know how much of a laborious work that is.
In addition, to download those dependencies, you have to keep track of their versions for your project to work. But that is where Maven comes to help.
It helps you to keep track of your dependencies and their respective versions along with downloading them when they are needed to build the project.
To install Maven, you need to have JDK (Java Development Kit) installed on your machine first. You can simply download the JDK binary file, and extract it or use an installer. After that, you can go to http://maven.apache.org, and click the Download link. You can either click the Download in the side panel or the Download, Install, Run Maven in the content section.
When you click that link, you will be redirected to the downloads page, where you will see the system requirements for Maven (Always try to download the latest version of Maven). The most important requirement here is the JDK version. Right now, the latest version is Maven 3.9.5 and the JDK version required for that is JDK 1.8.
And when you scroll down, you will see the installation instructions and the downloadable files. Here, we can select the apache-maven-3.9.5-bin.tar.gz to download. After downloading the binary zip file, extract it, and set the JAVA_HOME
and MAVEN_HOME
paths in the environment settings.
The following steps show how to install Maven to a MacOS that already has JDK >= 1.8 installed.
cd ~/Downloads
tar -xvf apache-maven-3.9.5.bin.tar.gz
/opt/
directory. → sudo mv ~/Downloads/apache-maven-3.9.5 /opt/
~/.zshrc
or ~/.bash_profile
in your Mac (Depending on the terminal you use) → open ~/.zshrc
Add the following two lines to the file. →
MAVEN_HOME="/opt/apache-maven-3.9.5"
export PATH="$MAVEN_HOME/bin:$PATH"
Finally, source the ~/.zshrc
or ~/.bash_profile
file. → source ~/.zshrc
After installing the Apache Maven, you can check whether it is working by using either of the following two commands.
mvn -v
mvn -version
The instructions on how to install Maven on Windows and Linux machines are explained in the following articles.
SDKMAN is a tool which allows you to download and manage SDKs. Using this, you are able to switch between multiple versions of the same SDK. To install SDKMAN, go to https://sdkman.io/.
Here, I will explain how to install it on a MacOS machine, and you can find the details on how to install it on Windows or Linux at their official site.
curl -s “https://get.sdkman.io” | bash
source “$HOME/.sdkman/bin/sdkman-init.sh
After that, open your ~/.zshrc
or ~/.bash_profile
to verify the following is included at the end of the file.
export SDKMAN_DIR="$HOME/.sdkman"
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"
After that, type the following to verify whether it is working. → sdk version
To install JDK with SDKMAN, follow the below instructions.
sdk list java
to list down all the Java versions that can be used with SDKMAN.
8.0.372-amzn
from Corretto).
sdk install 8.0.372-amzn
to install that version.
11.0.20-amzn
) then type and run sdk install 11.0.20-amzn
8.0.372-amzn
to 11.0.20-amzn
, type sdk use java 11.0.20-amzn
.
8.0.372-amzn
), you can simply type and run sdk uninstall java 8.0.372-amzn
Install Maven with SDKMAN is similar to how you installed the JDK with SDKMAN.
sdk list maven
to list down all the Maven versions that are available.
sdk install <mvn-version>
to install it.
sdk uninstall <mvn-version>
.
After installing both Java and Maven, you can check whether they are working by typing the below commands.
java -version
mvn -v
Once you start using Maven, you will notice that Maven has created local, user-specific configuration files and a local repository in the ~/.m2
directory. Inside that ~/.m2
there are,
~/.m2/settings.xml
→ A file containing the user-specific configurations for authentication, repositories, and other information to customize the behavior of Maven.
~/.m2/repository
→ This directory contains the local Maven repository. When you download a dependency from a remote Maven repository, Maven stores a copy of that dependency in your local repository.
Remote Maven repositories have the dependencies uploaded by certain organizations, and the local repository is the ~/.m2/repository
in our local machine.
For instance, if we need, org.apache.spark.spark-sql in our project, we can mention that to Maven, and it will download it from a remote repository like Maven Central(https://central.sonatype.com/) or MVN Repository(https://mvnrepository.com/) and put it inside the ~/.m2/repository
directory.
The project Object Model or the POM is an essential file in Maven Development since it declares the project’s identity and structure. This is usually described in XML. The POM file contains four categories of description and configuration.
General Project Information → This includes a project’s name, the URL for a project, the sponsoring organization, and a list of developers and contributors along with the license for a project.
Build Settings → This section can be used to customize the default Maven build. It is possible to change the location of resources and tests, add new plugins, and add new plugin goals to the lifecycle using this section.
Build Environment → The build environment has profiles that can be activated for use in different environments.
POM Relationships → Usually, a project depends on other projects and inherits POM settings from its parent project. These relationships are defined in this section.
All the Maven project POM files extend the Super POM, which defines a set of defaults shared by all projects. This file comes as a part of the installation and can be found in,
maven-<version>-uber.jar
file in ${M2_HOME}/lib
for Maven 2.
maven-model-builder-<version>.jar
file in ${M2_HOME}/lib
for Maven 3.
~/.sdkman/candidates/maven/<maven-version>/lib
Since all the Maven POMs inherit the defaults from the Super POM, if you want, you can,
src/main/java
src/test/java
mvn site
To do those things, you don’t have to customize anything. You can use a simple POM file like the one shown below.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mavensample.project</groupId>
<artifactId>simplest</artifactId>
<version>1</version>
</project>
Since this is the simplest POM file you can have, it is normally known as the Simplest POM. If you want to create a simple program that belongs to the above criteria, you can type and run mvn package
, and it will produce a JAR file in /target/simples-1.jar
The Simplest POM brings us over to the concept called Effective POM. The Effective POM is made of a combination of super POM and parent POM files. It is notable that the default configurations can be overridden by the parent POM files and the current project’s POM when creating an effective POM.
To find a project’s Effective POM, you’ll need to run the effective-pom
goal in Maven Help plugin using, mvn help:effective-pom
This will print out an XML document showing the merges between the aforementioned POM files. To use the mvn help:effective-pom
, you need to have the Maven Help plugin help goal defined. To install the Maven Help plugin, you can use the below code, and include that in your POM file.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-help-plugin</artifactId>
<version>3.4.0</version> <!-- Use the latest version -->
</plugin>
</plugins>
</build>
A Maven project’s version encodes a release version number that is used to group and order releases. A Maven version has the following parts.
These parts correspond to the following format.
<major version>.<minor version>.<incremental version>-qualifier
For example, the version 1.2.3
has a major version of 1, a minor version of 2, and an incremental version of 3. The version `1`
has a major version of 1 and no minor or incremental versions. The qualifier is used to capture the milestone builds like alpha and beta releases (1.2.3-beta-01
)
When using version build numbers like 1.1.2-beta-01
and 1.1.2-beta-10
you should always use left padding (01,02, etc.) to ensure Maven build number issues.
Maven versions also can contain a string literal to signify that a project is currently under active development by adding the string SNAPSHOT
. This string token will be expanded to a UTC(Coordinated Universal Time Value, e.g., 20230207-230803-1). (e.g., 1.0-SNAPSHOT
→1.0-20230207-230803-1
)
As a default setting, Maven will not check the SNAPSHOT
versions on remote repositories. To depend on SNAPSHOT
releases, you have to explicitly enable the ability to download them using a repository
or pluginRepository
element in POM.
Also, POM can include references to properties preceded by a dollar sign($
) and surrounded by two curly braces.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mavensample.project</groupId>
<artifactId>project-x</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<build>
<finalName>${project.groupId}-${project.artifactId}</finalName>
</build>
</project>
If you put the above in your pom.xml
file and run mvn help:effective-pom
, you will see the output contains the line, <finalName>org.mavensample.project-project-x</finalName>
Maven provides three implicit variables that can be used to access environment variables, POM information, and Maven Settings.
env
→This exposes the environment variables defined by your operating system or shell. For example, reference to ${env.JAVA_HOME}
would be replaced by the ${JAVA_HOME}
environment variable.
project
→This exposes the POM. You can use the dot-notation path references here.
settings
→This exposes the Maven settings information. You can use dot notation to refer to the values defined in thesettings.xml
file. For example, ${settings.offline}
would reference the value of the offline
element in ~/.m2/settings.xml
.
In addition to the above three, you can reference the system properties and the arbitrary properties set in the Maven POM or build profile.
getProperties()
defined on java.lang.System
.(e.g.,${user.name}
, ${java.home}
)
Arbitrary properties → These can be set with the <properties></properties>
element in the POM file or the settings.xml
or can be loaded from external files.
<properties>
<org.mavensample.project.version>1.0</org.mavensample.project.version>
</properties>
Maven can manage both internal and external dependencies. An external dependency for a Java project might be a library like Spring Framework or Log4j. An internal dependency for a Java project can be a web application depending on another project that contains the service class, model objects, or persistence logic.
When you define a dependency, you are using groupId
, artifactId
, version
, and classifier
to define a unique location of that dependency. This is also done when you are creating a new Maven project. These are called Maven Coordinates and they are integral when defining the dependencies.
There are five dependency scopes. These show what phases we are using the dependency.
compile
→compile
is the default scope; all the dependencies are compile
scoped if a scope is not defined. compile
dependencies are available in all classpaths, and they are packaged.
provided
→ provided
dependencies are used when you expect JDK or a container to provide them. For example, if you are developing a web application, you need the Servlet API available on the compile classpath to compile a servlet, but you don’t need to include the Servlet API in the packaged WAR file. In this case, provided
scope should be used. These dependencies are available on the compilation(not runtime) classpath. They are not transitive, nor they are packaged.
runtime
→ runtime
dependencies are required to execute and test the system, but they are not required for compilation. For example, you may need a JDBC API JAR at compile time and JDBC driver implementation only at runtime.
test
→ These dependencies are not required during the normal operation of an application and they are only needed during the test compilation and execution phases.
system
→ This scope is similar to provided
, but must specify the explicit path to the JAR on the local file system. If you declare the scope system
, you must also provide the systemPath
element. This is not a recommended scope to be used.When you need a dependency to compile a specific project but you don’t need it to be shown up as a transitive runtime dependency for the project that uses your specific project, you can use optional dependencies. You can use the <optional>true</optional>
for that.
Usually, you don’t need to use optional dependencies. Instead of one large project with a series of optional dependencies, you should always separate the project into submodules and use the specific dependency in the specific project.
In Maven, you don’t need to depend on a specific version of a dependency; you can specify a range of versions that would satisfy a given dependency.
(,)
→ exclusive quantifiers (e.g., <version>(1.0,3.0)</version>
).[,]
→ inclusive quantifiers (e.g., <version>[1.0,3.0]</version>
).<version>(1.0,3.0]</version>
).A transitive dependency is a dependency of a dependency. If project-a
depends on project-b
, which depends on project-c
, then project-c
is a transitive dependency of project-a
. If project-a
depends on project-d
, then it is also considered a transitive dependency of project-a
.
The best way to look at the transitive dependencies of a project is to type and run the following command.
mvn dependency:tree
Sometimes, you need to exclude a transitive dependency, such as when you depend 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. An example is shown below.
<dependency>
<groupId>org.mavensample.project</groupId>
<artifactId>project-x</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>org.mavensample.project</groupId>
<artifactId>project-y</artifactId>
</exclusion>
</exclusions>
</dependency>
If you have a large set of projects that use the same set of dependencies, you can define a dependency management block in your muti-module project’s top-level POM. An example is shown below of how you can use the MySQL Java connector version 5.1.2 in the top-level POM.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.2</version>
</dependency>
...
<dependencies>
</dependencyManagement>
Then you can use them in a child project, without using the version metadata or without declaring it since it is getting inherited.
A build lifecycle is an organized sequence of phases that can be used to give orders or goals. Those goals can be used in the project to do multiple tasks. There are three standard lifecycles in Maven.
clean
→ Running mvn clean
invokes this and the Clean plugin’s clean goal (clean:clean
) is bound to this.
default/build
→This is the general model of a build process for a software application. It contains several important phases like generate-resources
,test
,compile
,install
,deploy
site
→ This can be used to generate a site from the Maven project by running mvn site
.Profiles allow the user to customize a particular build for a particular environment. In other words, profiles enable portability between different environments. An example is shown below.
<profile>
<id>jdk16</id>
<activation>
<activeByDefault>false</activeByDefault>
<jdk>1.6</jdk>
<property>
<name>!environment.type</name>
</property>
</activation>
<modules>
<module>simple-script</module>
</modules>
</profile>
As you can notice activeByDefault
sets the default profile to be activated, and <property></property>
makes sure that the profile is activated when the environment.type
value is not present.
To list all the active profiles, you can use the following command.
mvn help:active-profiles
Usually, a POM can have several profiles to activate different environments like the example given below.
<profiles>
<profile>
<id>development</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>environment.type</name>
<value>dev</value>
</property>
</activation>
<properties>
<database.driverClassName>com.mysql.jdbc.Driver</database.driverClassName>
<database.url>jdbc:mysql://localhost:3306/app_dev</database.url>
<database.user>dev_user</database.user>
<database.password>deve_password</database.password>
</properties>
</profile>
<profile>
<id>production</id>
<activation>
<property>
<name>environment.type</name>
<value>prod</value>
</property>
</activation>
<properties>
<database.driverClassName>com.mysql.jdbc.Driver</database.driverClassName>
<database.url>jdbc:mysql://master01:3306,slave01:3306/app_prod</database.url>
<database.user>prod_user</database.user>
</properties>
</profile>
</profiles>
In such cases, you can activate the dev
profile by running mvn clean install -Denvironemtn.type=dev
When using Maven it is good to know the common Maven commands that you will be using day to day. A few of the most important ones that I am using day to day are listed below.
Display Maven version →
mvn -v
mvn --version
Getting help→
mvn -h
mvn --help
To build a Maven artifact → mvn clean install
To build a Maven artifact without running tests →
mvn clean install -Dmaven.test.skip=true
mvn clean install -DskipTests
To run Maven with a profile (e.g., production
profile) → mvn clean install -Pproduction
Profile activation with variables → mvn clean install -Denvironment.type=dev
Listing the active profiles → mvn help:active-profiles
To run Maven with debug enabled →
mvn clean install -X
mvn clean install --debug
To view the effective POM → mvn help:effective-pom
To view the dependency tree → mvn dependency:tree
To view execution error messages with full stack trace →
mvn -e clean install
mvn --errors clean install
To only show error messages →
mvn -q clean install
mvn --quiet clean install
Run Maven in offline mode → mvn -o clean install
Making a subset of projects → The projects that were mentioned will be built.
mvn --projects <project_name_1>,<project_name_2> --also-make install
mvn -pl <project_name_1>,<project_name_2> -am install
Making project dependents → The mentioned projects and their dependents will be built.
mvn --projects <project_name_1>,<project_name_2> --also-make-dependents install
mvn -pl <project_name_1>,<project_name_2> --amd install
Resuming a project → If you have built a few modules of a project and need to continue with other submodules, you can use this.
mvn --resumt-from <project_name> install
mvn -rf <project_name> install
Describe a Maven plugin →
mvn help:describe -Dplugin=help -Dfull
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin
Describe the plugin with goals → mvn help:describe -Dcmd=compiler:compiler
More information on the available configuration parameters → mvn help:describe -Dcmd=compiler:compile -Ddetail
So, this is it! Check out my previous article on Maven on Medium if you want to know how to use Maven with Windows! (This is the original article, this article was based upon. 🙃) If you want to know all about Maven, check out Maven: The Complete Reference by SonaType. Most of the concepts I have described here are mentioned thoroughly in this book.
I have not mentioned about plugins
in this article, since it is a heavy topic. But if you are interested in learning about Maven Plugins, check out the below link.
https://maven.apache.org/guides/plugin/guide-java-plugin-development.html
Cheers!