Gradle is a great tool, but not the only one. The most popular is still Maven. So if you already know Gradle, but want to grasp the basics of the Maven, this bonus chapter is for you!
Use this guide to install Maven.
Also you can use package managers:
- Windows:
choco install maven
- Mac:
brew install maven
- Ubuntu:
apt install maven
To check if Maven was installed correctly: mvn --version
Maven is a project development management and comprehension tool with a lot of useful features:
- builds
- dependency management
- documentation creation
- site publication
- distribution publication, etc.
Both Gradle and Maven has its advantages and disadvantages. However, this chapter is more focused on functionality differences, for example, how to create a project in Maven, or what build script Maven has.
🐘 / 🪶 | Gradle | Maven |
---|---|---|
Installation | installation is necessary for project creation, but unnecessary for starting a project due to Wrapper functionality | user has to have Maven installed in order to create or start Maven project (unless this Maven project has generated Wrapper files, then user can run project without locally installed Maven) |
Creation | gradle init command starts menu where user can choose project options (like base language, test dependency, as well as name of the project, etc) |
mvn archetype:generate starts a menu where user can choose type of archetype (= type of project, like webapp, spring project, etc) and project values (like package, version, etc). User can also skip the menu, and configure all the parameters as one command, e.g.: mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 |
IDE support | is supported by popular IDEs like Eclipse or IntelliJ IDEA | is supported by popular IDEs like Eclipse or IntelliJ IDEA |
Project structure | typical project structure has build.gradle file, wrapper files and settings.gradle file. Source code/test/resources, etc by default are located in src directory, build output files - in build directory |
default project structure has pom.xml file, src directory for source code/test/resources, etc, and target directory for build output files |
Configuration | build.gradle file written in Kotlin or Groovy-based DSL + settings.gradle file + gradle.properties file (for configuring behavior of Gradle itself) |
pom.xml file written in XML. User can also specify user configuration in settings.xml file if necessary |
Commands to work with the tool | CLI with build-in + custom tasks (task as a main concept of some small action to execute). To run a task, use gradle <TASK> command; or ./gradlew <TASK> (Linux/Mac) and ./gradlew.bat <TASK> (Windows) to use Wrapper |
lifecycle phases and plugin goals as a main concept of some small action to execute. Running a phase will also execute all preceding phases. To run a phase: mvn <PHASE> , to run a specific goal: mvn <PLUGIN>:<GOAL> . To use wrapper scripts: instead of mvn , use ./mvnw for Linux/Mac or mvnw.cmd for Windows. |
Plugins | core and community-made plugins (collection of tasks) for extending functionality | adding or reconfiguring plugins (collection of goals) to customise the build for a Maven project |
Multi-project | root project has settings.gradle file to specify what subprojects to include into one build (but no build.gradle file in root); each subproject has its own build.gradle file |
root project has a pom.xml file that lists all submodules, and its packaging type is pom . Each submodule has its own pom.xml file with parent section in it |
Build output files | jar, archives, test reports and other build output are automatically generated into build directory, for example, after build task |
all build files are generated into target directory. It is a good practice to clean this directory before each build |
Wrapper | wrapper files are automatically generated when creating a project with gradle init task. to run a task using wrapper, use commands ./gradlew <TASK> (Linux/Mac) or ./gradlew.bat <TASK> (Windows) |
you should generate wrapper files manually by using command mvn wrapper:wrapper . To run a command with Wrapper, instead of mvn , type ./mvnw (Linux/Mac) or mvnw.cmd (Windows) |
- user can create new project with the help of Archetype plugin
- in simple words, archetype is a project template
- additive mechanism: you can use archetype on the already generated by another archetype project. For example, user generated project with the
quick start
archetype, and wants to create a site for that project. He can use thesite
archetype within that existing project
To generate a project using archetype:
- run the following command in the project directory:
mvn archetype:generate
- choose the archetype from the internal catalog
- enter the values for the
groupId
, theartifactId
and theversion
(default is1.0-SNAPSHOT
) of the project and the base package for the sources - confirm
It is also possible to pass all the parameters to the command immediately, for example:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
🔎 Check also: guide to naming conventions on groupId, artifactId, and version.
Default archetype is quickstart archetype.
🔎 Check also: list of archetypes.
You may want to standardize J2EE development within your organization, so you may want to provide your own archetypes. Once archetype is created and deployed in your organization's repository, it is available for use by all developers within your organization. Guide to creating Archetypes.
Maven is integrated into Apache NetBeans IDE, Eclipse IDE and JetBrains IntelliJ IDEA.
🔎 Read more about IDE Integration.
After running a command for creating a project:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
you will see the following project structure:
my-app
├── pom.xml
└── src
├── main
| └── java
| └── com
| └── mycompany
| └── app
| └── App.java
└── test
└── java
└── com
└── mycompany
└── app
└── AppTest.java
pom.xml
is a file for configuring a projectsrc
directory is a place for project sources (tests, source code, resources, etc)
Maven has its conventions for default project structure (however, they can be overridden via the project descriptor):
- At the top level of any project:
pom.xml
- In addition, there can be textual documents:
LICENSE.txt
- project's licenseNOTICE.txt
- notices and attributions required by libraries that the project depends onREADME.txt
- project's readme
- In the directory where
pom.xml
is located, there are two more directories:src
and (possibly)target
target
is used to house all output of the build. For example, after you compile the sources withmvn compile
, compiled classes will be placed into thetarget/classes
directorysrc
contains all of the source materials (e.g. code, resources) for building the project, its site and so on
🔎 Check also: more about src standard layout.
Maven configuration occurs at 3 levels:
- Project - most static configuration occurs in
pom.xml
- Installation - this is configuration added once for a Maven installation
- User - this is configuration specific to a particular user. You can specify them in
${user.home}/.m2/settings.xml
The Project defines information that applies to the project, no matter who is building it, while Installation and User define settings for the current environment.
🔎 See more information here.
A little bit on XML sytax:
- XML documents must contain one root element:
<root>
<child>
<subchild>.....</subchild>
</child>
</root>
- All elements must have a closing tag:
<p>This is a paragraph.</p>
- XML tags are case sensitive. The tag is different from the tag . Opening and closing tags must be written with the same case
- All elements must be properly nested within each other, meaning that you should keep an order of closing tags according to the order of opening tags (last opening - first closing)
- XML elements can have attributes in name/value pairs, the attribute values must always be quoted:
<note date="12/11/2007">
<to>Tove</to>
<from>Jani</from>
</note>
- The syntax for writing comments:
<!-- This is a comment -->
- Some characters (
<
,>
,&
,'
,"
) have special meaning and cannot be written to XML elements as they are. Those characters must be replaced by entity reference (<
-><
,>
->>
,&
->&
,'
->'
,"
->"
):
<!-- Will generate error -->
<message>salary < 1000</message>
<!-- Correct -->
<message>salary < 1000</message>
Now back to pom.xml
.
A Project Object Model or POM is an XML file that contains information about the project and configuration details used by Maven to execute a task or goal.
Each project has its POM, but there is also the Super POM - Maven's default POM. All POMs extend the Super POM unless explicitly set, meaning the configuration specified in the Super POM is inherited by the POMs you created for your projects.
The minimum required elements for a POM are:
project
- rootmodelVersion
- should be set to 4.0.0groupId
- the id of the project's groupartifactId
- the id of the artifact (project)version
- the version of the artifact under the specified group
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
The last three values form the project's fully qualified artifact name in the form of <groupId>:<artifactId>:<version>
.
If the configuration details are not specified, Maven will use defaults or inherit from the Super POM:
- if packaging type is not specified in the POM, then the default value
jar
would be used - if
repositories
were not specified, Maven will use the Super POMrepositories
configuration
List of elements configurable in POM:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- The Basics -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>...</packaging>
<dependencies>...</dependencies>
<parent>...</parent>
<dependencyManagement>...</dependencyManagement>
<modules>...</modules>
<properties>...</properties>
<!-- Build Settings -->
<build>...</build>
<reporting>...</reporting>
<!-- More Project Information -->
<name>...</name>
<description>...</description>
<url>...</url>
<inceptionYear>...</inceptionYear>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- Environment Settings -->
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement>
<mailingLists>...</mailingLists>
<scm>...</scm>
<prerequisites>...</prerequisites>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<distributionManagement>...</distributionManagement>
<profiles>...</profiles>
</project>
🔎 To get more details on these elements, check this page.
The Basics:
packaging
- default:jar
, other available:pom
,jar
,maven-plugin
,ejb
,war
,ear
,rar
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<packaging>war</packaging>
...
</project>
dependencies
element is a list of dependencies. Each dependency contain information about it, likegroupId
,artifactId
,version
,type
,scope
, etc:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<type>jar</type>
<scope>test</scope>
<optional>true</optional>
</dependency>
...
</dependencies>
...
</project>
properties
element is for value placeholders. Those values are accessible anywhere within a POM by using the notation${X}
, whereX
is the property:
<properties>
<jackson.version>2.10.2</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependencies>
Or they can be used by plugins as default values. For example, the Compiler Plugin can be configured to compile a certain project to a different version than what you are currently using.
If you want to use the Java 8 language features (-source
1.8) and also want the compiled classes to be compatible with JVM 1.8 (-target
1.8), you can either add the two following properties, which are the default property names for the plugin parameters:
<project>
[...]
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
[...]
</project>
or configure the plugin directly:
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
[...]
</build>
[...]
</project>
🔎 More on The Basics here.
Build Settings:
Those settings consists of the build
element, that handles things like declaring your project's directory structure and managing plugins; and the reporting
element, that largely mirrors the build element for reporting purposes.
BaseBuild
is the base set of elements:
<build>
<defaultGoal>install</defaultGoal>
<directory>${basedir}/target</directory>
<finalName>${artifactId}-${version}</finalName>
<filters>
<filter>filters/filter1.properties</filter>
</filters>
...
</build>
defaultGoal
: the default goal or phase to execute if none is given
directory
: build output directory
finalName
: the name of the bundled project when it is finally built
filter
: defines *.properties files that contain a list of properties that apply to resources
🔎 More on Build Settings here.
More Project Information:
Here is where you can configure description of the project, its url, as well as licenses, developing organization and developers themselves.
🔎 More on More project information here.
Environment Settings:
Settings for managing project environment, for example, continuous integration build systems, mailing lists, repositories for plugins or dependencies, etc.
🔎 More on Environment Settings here.
Maven is based around the concept of a build lifecycle - process for building and distributing a particular project.
There are three built-in build lifecycles:
default
- main lifecycle, handles your project deploymentclean
- handles project cleaningsite
- handles the creation of your project's web site
Each of these build lifecycles consists of phases. In other words, a phase is a stage of a build lyfecycle. They are sequential (have a specific order). So if we execute one phase, all the preceding phases will be executed as well (for example, if we run the deploy phase, which is the last phase in the default build lifecycle, it’ll execute all the phases before the deploy phase as well, which is the entire default lifecycle).
To run a phase, use command mvn <PHASE>
, for example, mvn deploy
.
Note that you can also use wrapper scripts to run commands: instead of mvn
, use ./mvnw
for Linux/Mac or mvnw.cmd
for Windows.
Some of the most popular phases of default lifecycle (in order):
validate
- validate the project is correct and all necessary information is availablecompile
- compile the source code of the projecttest
- test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployedpackage
- take the compiled code and package it in its distributable format, such as a JARverify
- run any checks on results of integration tests to ensure quality criteria are metinstall
- install the package into the local repository to use as a dependency in other projects locallydeploy
- done in the build environment, copies the final package to the remote repository for sharing with other developers and projects
Clean lifecycle:
clean
- remove thetarget
directory with all the build data
Site lifecycle:
site
- generate a web site for your project
🔎 Check also: full list of build phases of 3 lifecycles.
So if you run mvn verify
, it will execute every preceding phase before executing verify
. And command mvn deploy
will execute the entire default lifecycle.
It is possible to combine phases of different lifecycles in one command: mvn clean deploy
- Maven traverses into every subproject and executes clean
, then executes deploy
(including all of the prior build phases).
Each phase is a sequence of goals, so each goal is responsible for a specific task.
When we run a phase, all goals are executed in order.
Goals are part of the plugins, so to execute one goal, you need to mention a name of a plugin, too (in a format <PLUGIN>:<GOAL>
).
For example, package phase contains 2 goals: jar:jar
and war:war
.
compile phase consists of compiler:compile
goal.
To list all the goals bound to the specific phase: mvn help:describe -Dcmd=<PHASENAME>
A goal may be bound to zero or more build phases. A goal not bound to any build phase could be executed outside of the build lifecycle by direct invocation.
To run a specific goal without executing its entire phase (and the preceding phases), use the command: mvn <PLUGIN>:<GOAL>
Note: the phases named with hyphenated-words (pre-*
, post-*
, or process-*
) are not usually directly called from the command line, since they are producing intermediate results that are not useful outside the build.
An interesting thing to note is that phases and goals may be executed in sequence.
mvn clean dependency:copy-dependencies package
This command will clean
the project, copy dependencies
, and package
the project (executing all of the phases up to package
, of course).
How to bind goals to phases?
- With packages. Each packaging contains a list of goals to bind to a particular phase. For example,
jar
packaging will bind the following goals to the phases of default lifecycle:
Phase | Plugin:Goal |
---|---|
process-resources | resources:resources |
compile | compiler:compile |
process-test-resources | resources:testResources |
test-compile | compiler:testCompile |
test | surefire:test |
package | jar:jar |
install | install:install |
deploy | deploy:deploy |
🔎 You can check default bindings for different packages here.
- With plugins. Plugin itself is a collection of goals, so by configuring plugins, you can add goals to the build. You need to add the plugin to the POM in the
<plugins>
section of<build>
.
For example, let's say you have a goal display:time
that echos the current time to the commandline, and you want it to run in the process-test-resources
phase to indicate when the tests were started. This would be configured like so:
<plugin>
<groupId>com.mycompany.example</groupId>
<artifactId>display-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>time</goal>
</goals>
</execution>
</executions>
</plugin>
A Maven plugin is a group of goals; however, these goals aren’t necessarily all bound to the same phase.
To list all goals in a specific plugin: mvn <PLUGIN>:help
.
To see a description of the plugin and its parameters and types:
mvn <PLUGIN>:help -Ddetail -Dgoal=<GOAL>
There are two types of plugins, build and reporting:
- Build plugins are executed during the build and configured in the
<build/>
POM element. - Reporting plugins are executed during the site generation and configured in the
<reporting/>
element.
All plugins should have minimal required information: groupId
, artifactId
and version
.
Ways to use a plugin:
- add it to the POM and use it as it is
- add and configure the plugin as needed:
- as described in the previous section, it is possible to add new goals to the process, configure specific goals, bind them to phases
- or you can configure specific parameters, that will be applied to all the goals of the plugin
For example, to configure the Java compiler to allow JDK 5.0 sources, add configuration
element, which applies the given parameters to every goal from the compiler
plugin:
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
...
About pluginManagement
element:
You can add and configure plugins either in plugins
section of the POM, or in pluginManagement
section. The difference is that by adding the plugin in the pluginManagement
section, it becomes available to this POM, and all inheriting child POMs. Then in child POM, you don't need to duplicate the configurations.
🔎 You can read more about configuring plugins here.
- A multi-module project contains an aggregator POM (the POM where all submodules listed), and the submodules (projects).
- Each project can be build separately, or together through the the aggregator POM.
- Each project has its own POM, that can inherit some shared configurations from aggregator POM.
How to create a multi-module project:
- generate root project:
mvn archetype:generate -DgroupId=com.example -DartifactId=parent-project
- change packaging type to
pom
so it won't produce any further artifacts and will serve as parent and aggregator POM:
<packaging>pom</packaging>
- go to the directory of root project (in this case to
parent-project
directory), and create submodules you need with the help ofarchetype:generate
goal (note that you can add more parameters to the command depending on your project):
cd parent-project
mvn archetype:generate -DgroupId=com.example -DartifactId=service
mvn archetype:generate -DgroupId=com.example -DartifactId=webapp
- done!
Since packaging type of parent-project
is pom
, Maven will recognise all new created projects (service
and webapp
) as submodules. pom.xml
files will be modified accordingly:
parent-project
pom.xml file:
<modules>
<module>service</module>
<module>webapp</module>
</modules>
service
andwebapp
pom.xml files:
<parent>
<artifactId>parent-project</artifactId>
<groupId>com.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
To build all projects at once, go to the parent directory and run:
mvn package
If you want to share dependencies in all submodules, use dependencyManagement
section in root pom.xml
(in this case, in POM located in parent-project
directory):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.16</version>
</dependency>
//...
</dependencies>
</dependencyManagement>
Now, if you want to inherit this dependency by any submodule, you can simply specify groupId
and artifactId
parameters in submodule's POM (version
and scope
will be inherited):
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
//...
</dependencies>
If you specify version
parameter here, too, then parent's version will be overridden, and this specific submodule will use specified version.
All the files generated by build are located in target
directory. For example, target/classes
contains compiled classes, similarly, target/test-classes
contains compiled classes used for tests. Package file (e.g.: .jar
) will be generated directly into the target
directory. What gets generated in it during the build depends only on POM and how your build is happening.
Those files are temporary, so it is a good practice to run mvn clean
to delete them before each build (not doing it can lead to weird compile behaviour, like being able to compile code that shouldn't be).
There is a difference between temporary and final build artifacts:
mvn package
command will generate a file totarget
directory. Name of the file can be either custom, set within the POM, or default (composed from Maven coordinates of the project). This file will contain compiled classes, you can send it to anybody, but in general it is a temporary file.mvn install
will install the package to the local repository (~/.m2/repository/
), and you will be able to use this package as a dependency in other projects locally. This file is not temporary, and it contains generated artifacts + project's POM. Name of this file cannot be custom. For example, a project with a group id ofmy.groupid
, an artifact id ofmy-artifactid
and a version of1.0
will get installed in the foldermy/groupid/my-artifactid/1.0
; in which you'll find the POM file, and all the other artifacts. The name of the artifacts themselves cannot be overridden: it will bemy-artifactid-1.0.jar
for a JAR file.
Wrapper provides a fully encapsulated build setup.
To add or update all the necessary Maven Wrapper files to your project, use the command:
mvn wrapper:wrapper
Wrapper files will appear. And now, to run a command, you can use wrapper scripts:
./mvnw
(Linux/Mac) or mvnw.cmd
(Windows). For example:
./mvnw clean install
So if the user doesn't have the necessary version of Maven specified in .mvn/wrapper/maven-wrapper.properties
, it will be downloaded, installed and then used.
Subsequent uses of mvnw
/mvnw.cmd
will use the previously downloaded specific version as needed.
⬅️ BACK | TABLE OF CONTENTS | NEXT ➡️ |
---|