Время на прочтение
6 мин
Количество просмотров 515K
После публикации топика о Maven в комментариях возникли вопросы о том, как начать с ним работать, с чего начать, как составлять файлы pom.xml, откуда брать плагины и т.п. Данный топик будет своего рода getting started или f.a.q.
Терминология
Как в любой системе, в Maven, есть свой набор терминов и понятий.
Вся структура проекта описывается в файле pom.xml (POM – Project Object Model), который должен находиться в корневой папке проекта. Ключевым понятием Maven является артефакт — это, по сути, любая библиотека, хранящаяся в репозитории. Это может быть какая-то зависимость или плагин.
Зависимости — это те библиотеки, которые непосредственно используются в вашем проекте для компиляции кода или его тестирования.
Плагины же используются самим Maven’ом при сборке проекта или для каких-то других целей (деплоймент, создание файлов проекта для Eclipse и др.).
В самом начале работы с Maven, пользователь непременно столкнется с таким понятием как архетип. Архетип — это некая стандартная компоновка файлов и каталогов в проектах различного рода (веб, swing-проекты и прочие). Другими словами, Maven знает, как обычно строятся проекты и в соответствии с архетипом создает структуру каталогов.
Как правило, название артефакта состоит из названия группы, собственного названия и версии. К примеру Spring будет иметь вот такое название в среде Maven: org.springframework.spring:2.5.5. Последний домен означает всегда artifactId, все, что перед ним – groupId – хорошо это запомните!
На жизненном цикле останавливаться не буду, так как он хорошо описан в вышеобозначенной статье. А теперь перейдем к практике.
Установка Maven
Последнюю версию всегда можно скачать на странице загрузки на официальном сайте. Просто распаковываем архив в любую директорию. Далее необходимо создать переменную в Path, в которой необходимо указать путь к Maven. Заходим в Win + Pause – Дополнительно – Переменные среды – в верхнем окошке нажимаем Создать, вводим имя M2_HOME и значение допустим “C:apache-maven-2.2.1”. Далее там же создаем еще одну переменную M2 со значением %M2_HOME%bin. Так же убеждаемся, что есть переменная JAVA_HOME с путем к JDK. Ее значение должно быть примерно таким «c:Program FilesJavajdk1.6.0_10». И наконец в том же окошке создаем/модифицируем переменную Path, в нее необходимо просто написать %M2%, чтобы наша папочка с исполняемым файлом Maven была видна из командной строки. Теперь необходимо проверить работоспособность нашей установки. Для этого заходим в командную строку и вводим команду
mvn –version
Должна появиться информация о версиях Maven, jre и операционной системе, что-то вроде:
Maven version: 2.2.1 Java version: 1.6.0_10 OS name: "windows 2003" version: "5.2" arch: "x86" Family: "windows"
Maven создаст вам локальный репозиторий в вашей личной папке, например в каталоге C:Documents and Settingsusername.m2repository
Все, Maven готов к работе, можно приступать к созданию приложения.
Создание приложения из архетипа
На сайте Maven перечислены наиболее популярные архетипы для приложений, но вы можете легко создать свой или найти более специфичный например здесь.
Итак, допустим нас интересует веб-приложение – находим подходящий архетип, называется он maven-archetype-webapp. В командной строке, в необходимом каталоге выполняем команду Maven:
mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=my-webapp -DarchetypeArtifactId=maven-archetype-webapp
Теперь мы можем лицезреть довольно наглядную структуру каталогов с говорящими названиями java – здесь будут ваши классы, webapp – здесь размещаются странички веб-приложения, resources – различного рода ресурсы в classpath (файлы конфигурации, например), test – юнит-тесты, соответственно и т.п.
Сборка проекта
Здесь все просто – выполняем команду
mvn package
или
mvn install
в корневом каталоге приложения, там, где находится файл pom.xml. Первая команда скомпилирует ваш проект и поместит его в папку target, а вторая еще и положит его к вам в локальный репозиторий.
Есть полезная функция, наподобие конвеера, то есть можно написать
mvn clean install
и Maven сначала очистит папку target проекта, потом соберет его и положит в репозиторий.
Минимальный набор действий для работы Maven мы изучили, теперь переходим к кастомизации и добавлению зависимостей проекта.
Зависимости и репозитории
Как правило, большинство популярных библиотек находятся в центральном репозитории, поэтому их можно прописывать сразу в раздел dependencies вашего pom-файла. Например чтобы подключить Spring Framework необходимо определить следующую зависимость:
<dependencies> ... <dependency> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> <version>2.5.5</version> </dependency> </dependencies>
Версию хотя и можно не указывать и тогда Maven возьмет последний вариант, но я вам лично советую это делать, потому как у нас неоднократно бывали случаи, что проект просто в один момент переставал собираться или начинал падать в совершенно неожиданных и хорошо оттестированных местах, хотя вроде бы никто ничего не менял.
Специфические вещи обычно не находятся в центральном репозитории и тогда вам придется указать репозиторий производителя вручную. Для примера добавим зависимость JSF-фреймворка ajax-компонентов JBoss RichFaces.
С зависимостями все просто:
<dependencies> <dependency> <groupId>org.richfaces.ui</groupId> <artifactId>richfaces-ui</artifactId> <version>3.3.1.GA</version> </dependency> </dependencies>
А вот репозиторий JBoss теперь необходимо прописать ручками либо в файле settings.xml, который лежит в корне вашего локального репозитория, либо в самом файле pom.xml, в зависимости от того, нужен ли данный репозиторий во всех проектах, либо в каком-то одном конкретном, соответственно:
<!-- JBoss RichFaces Repository --> <repositories> <repository> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> <updatePolicy>never</updatePolicy> </snapshots> <id>repository.jboss.com</id> <name>Jboss Repository for Maven</name> <url> http://repository.jboss.com/maven2/ </url> <layout>default</layout> </repository> </repositories>
Как правило на сайтах крупных проектов пишут всю информацию, необходимую для встраивания их библиотеки в проект на основе Maven, но бывают случаи, когда артефакт приходится искать очень и очень долго. Здесь нам очень сильно может помочь MVNrepository.com — он вам всегда подскажет где может находиться искомая библиотечка. Но если уж и там не нашлось, то из собственного опыта могу посоветовать гуглить «<название библиотеки> pom.xml». Бывает так, что проекты уже давно закрыты и в репозитории не положены потому что разработчики уже не заботятся об их распространении. Тогда остается один единственный способ – добавить файл в репозиторий вручную командой:
mvn install:install-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version> -Dpackaging=<packaging>
Последний параметр чаще всего имеет значение jar.
Плагины
Так как плагины являются такими же артефактами, как и зависимости, то они описываются практически так же. Вместо раздела dependencies – plugins, dependency – plugin, repositories – pluginRepositories, repository – pluginRepository.
Плагинами Maven делает все, даже непосредственно то, для чего он затевался – сборку проекта, только этот плагин необязательно указывать в свойствах проекта, если вы не хотите добавить какие-то фичи.
Посмотрим как настроить плагин для создания проекта для Eclipse с использованием WTP ver. 2.0. В раздел plugins нашего pom.xml прописываем следующий плагин:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-eclipse-plugin</artifactId> <configuration> <wtpversion>2.0</wtpversion> </configuration> </plugin>
Теперь идем опять таки в командную строку и выполняем команду
mvn eclipse:eclipse
Ждем пока Maven найдет все библиотеки в репозитории или скачает их и вуаля – теперь наш Maven-проект можно открыть как проект eclipse. При этом библиотеки никуда не копируются как при классическом подходе, а остаются в репозитории и Eclipse делает на них ссылку через свои переменные.
Единого списка всех плагинов естественно не существует, на официальном сайте только есть поддерживаемые плагины непосредственно разработчиками Maven. Однако хотелось бы отметить, что названия плагинов довольно прямолинейны и сделав поиск по ключевым словам «maven tomcat plugin» вы скорее всего обнаружите первой ссылкой плагин для деплоймента проекта в Tomcat.
Собственный репозиторий
К сожалению сам не имею большого опыта настройки репозитория, но могу посоветовать как наиболее простой и распространенный Nexus. За дополнительной информацией следует обратиться на сайт данного проекта.
Однако нельзя оставить без внимания и достойных конкурентов в лице Artifactory и Archiva.
Заключение
Очень надеюсь, что цель данной статьи достигнута – минимальных знаний по Maven должно хватить на работу с не очень большими проектами. Для более серьезных целей очень советую детально изучить maven-compiler-plugin и maven-resource-plugin – они напрямую отвечают за конечную компоновку проекта. Я считаю, что самым главным моментом в обучении Maven является понимание идеологии – Maven описывает конечную структуру проекта, а не пути к ее достижению, в отличие от Ant.
Полезные ссылки
Официальная страница Maven
Документация
Центральный репозиторий
Репозиторий iBiblio
Поиск артефактов по названию
Неплохой форум по Maven
Maven: The Definitive Guide — хорошая книга по Maven
This post was originally published in programmingtechie.com
If you are a Java Developer, your day to day activities while working on Java Projects includes writing code, compiling code, testing, packaging code in the form of an artifact like JAR, WAR or EAR and then deploying this artifact to an Application Server.
Apache Maven automates all the tasks which are mentioned above by minimizing the manual overhead.
In this tutorial, we will understand What is Apache Maven? and will cover all the concepts required for you to start using Maven in your Java Projects
If you are a visual learner like me, check out the below video tutorial:
Table of Contents
- What is Apache Maven
- Installing Maven
- Configure Maven Installation in IDE
- Create your First Maven Project
- Exploring Maven Folder Structure
-
Maven Core Concepts
-
Project Object Model
- Simple POM File
- Super POM File
- Effective POM File
-
Dependencies
- Transitive Dependencies
- Excluding Dependencies
- SNAPSHOT and RELEASE dependencies
- Dependency Scopes
-
Repositories
- Snapshot and Release Version Handling
-
Project Object Model
- Maven Build Lifecycle
-
Plugins and Goals
- Maven Compile Plugin
- Maven Surefire Plugin
- Maven Install Plugin
- Maven Deploy Plugin
- Other Maven Plugins
-
Maven Multi Module Projects
- Creating Multi Module Project using IntelliJ
- Managing Dependencies inside Maven Multi Module Project
- Managing Plugins inside Maven Multi Module Project
- Using Profiles
- Conclusion
What is Apache Maven ?
Apache Maven is a Project Management Tool used to manage projects which are developed using JVM languages like Java, Scala, Groovy etc.
The major tasks of a Project Management Tool include:
- Building the Source Code
- Testing Source Code
- Packaging the Source Code into an Artifact (ZIP, JAR, WAR or EAR)
- Handles Versioning and Releases of the Artifacts
- Generating JavaDocs from the Source Code
- Managing Project Dependencies
Maven is also called as a Build Tool or a Dependency Management Tool
Installing Maven
You can download Maven from the website here. At the time of writing this tutorial, the latest version of Maven is 3.6.3.
Under the Files section, you can download Maven by clicking on the link which looks something like apache-maven-3.6.3-bin.zip
Once you have downloaded and unzipped the folder, make sure to add the M2_HOME environment variable and to set the value of this variable to location of the unzipped folder.
After that make sure to set another environment variable called M2 which contains the value M2_HOME/bin
After that, make sure to update the Path variable also with the M2 environment variable.
Once you completed the above steps, open a new terminal window and type mvn –version
and you should see the output like below:
Configure Maven Installation in IDE
The next step is to configure the maven installation inside your favorite IDE, in this tutorial we are going to use IntelliJ IDEA as the primary IDE.
You can download the community edition of this IDE here
Once you have installed the IDE, on the Welcome Window click on the Configure button and select Settings and inside the Settings Window search for Maven and under the Maven home directory choose the Maven directory.
Click on Apply and then OK
You configured Maven successfully in your IDE. Now it’s time to create our first Maven Project.
Create your First Maven Project
In IntelliJ, click on New Project and in the window, select Maven to the left side and click on Next.
Then enter your project name and if you expand the section Artifact Coordinates.
You will see the fields GroupId, ArtifactId and Version.
- A GroupId is like a identifier for your project which usually follows the Java Package naming convention, so in our example IntelliJ has by default added the value org.example but you can add any value you like.
- An ArtifactId is the name of the project you are creating
- A Version is a unique number which identifies the version of our project.
Once you click on Finish, IntelliJ should open the project and the folder structure of the project will look something like the picture you see below.
Exploring Maven Folder Structure
Let’s have a look at the Folder Structure of the Maven Project we just created.
The src folder is the root for our application source code and tests. Then we have the following subfolders:
- The folder src/main/java contains the java source code, all the production code for our application resides here
- In the src/main/resources we will store all the files we are going to use in our project (example: Property Files, any files where we need to read in our application like XML, CSV etc.). If you are developing a web-application we will usually place all the static resources inside this folder.
- In the src/test/java folder we will store all the test classes in our project.
- There is another folder called target (currently not visible), which stores the compiled java class files. We will discuss about this in the coming sections
- And lastly, we have the pom.xml file which contains the metadata of the project dependencies.
Maven Core Concepts
Project Object Model
The Project Object Model File(also called as pom.xml) contains the meta-data of the project and is also responsible to manage dependencies and to configure plugins which helps us in automating many repetitive tasks.
Here is how a basic pom.xml file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven-project</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
Enter fullscreen mode
Exit fullscreen mode
- project is the top level tag of our pom.xml file, which encapsulates all the information related to our Maven Project.
- modelVersion represents what version of POM you are using. The modelVersion for Maven 3 is always 4.0. This will never change unless you are using another major version of Maven.
Now as we know what is a POM file, let’s have a look at different type’s of POM files. We have basically 3 types of POM files:
- Simple POM file
- Super POM file
- Effective POM file
Simple POM File
This is the same file(pom.xml) which was generated in the previous section. It only contains information which is relevant to our current project.
Super POM File
A Super POM File is a parent for all the Simple POM files, it contains a set of default configuration which is shared by all the Simple POM files.
You can find the Super POM file inside the Maven installation directory under the path M2_HOME/lib and inside the JAR file maven-model-builder-XXX.jar where XXX represents the version of the maven version you are using.
We can find it under the package name org.apache.maven.model under the file name pom-4.0.0.xml.
CAUTION: You should never try to edit the Super POM as it contains sensible defaults from Maven.
Effective POM File
An Effective POM File is nothing but a combination of Simple POM and Super POM File’s.
It’s just a way of checking all the information of the pom.xml files in one place.
We will have a look at how this will be helpful in the upcoming sections.
You can have a look at the Effective POM of our pom.xml file by typing the following command
mvn help:effective-pom
In IntelliJ, you can simply right click on the pom.xml file, select Maven -> Show Effective POM
Dependencies
If you are working on any non-trivial Java Project chances are that you are using many third party JAR files in your project to develop the application. These JAR files can be anything like a Framework or a Library. Examples include Junit, Spring Framework, Selenium etc.
These external libraries are called as dependencies. Maven provides an excellent way to specify and manage dependencies in our pom.xml file.
Without using Maven, you have to manually download the required JAR files from internet and add them one-by-one to the classpath of our project.
Maven provides a dependencies section inside the pom.xml where you can specify the information of the JAR you require in your project (groupId, artifactId and version). Once you have specified the required libraries, maven will automatically download these dependencies into our project and adds them to the classpath of our project.
Inside the dependencies section you can define each individual dependency like below inside the pom.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven-project</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
</dependency>
</dependencies>
</project>
Enter fullscreen mode
Exit fullscreen mode
We have defined Junit5 as a dependency in our pom.xml file.
Notice that I have mentioned the tags groupId, artifactId and version to uniquely identify a dependency and by providing these values, Maven can automatically download these dependencies to our project.
In IntelliJ, make sure to click on the Refresh button under the Maven Tab in the right side, to force IntelliJ to initiate the download of the dependencies. Check the below image:
In this way, you can provide all the dependencies needed in your project inside the pom.xml and Maven will automatically download them.
Transitive Dependencies
In the above example, you can observe that the junit-jupiter-engine indeed depends on other JAR files like apiguardian-api, junit-platform-engine and junit-jupiter-api
These JAR files are called Transitive Dependencies
Excluding Dependencies
There are some instances where we will have some conflicts between the project dependencies and the transitive dependencies, at that time we can manually exclude the transitive dependency we don’t need.
For example, if you are developing your application using Spring Boot, there is a dependency called spring-boot-starter-test which will provide all the dependencies needed to test the Spring Boot applications.
This spring-boot-starter-test dependency contains Junit 4 as the transitive dependency, as we are already using Junit 5 we can exclude the transitive dependency like below:
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven-project</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.6.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Enter fullscreen mode
Exit fullscreen mode
NOTE: The newer Spring Boot versions comes with JUnit 5 dependency, I purposely used the old version 2.1.6.RELEASE as an example.
SNAPSHOT and RELEASE dependencies
A dependency can be categorized into two ways:
- SNAPSHOT
- RELEASE
A Snapshot Dependency resembles that the project version is under development.
If you are working on a Java project in a team, chances are that you are following some kind of iterative process where you go through the phases of development and then release the software at the end of the phase.
When the project is under development we generally use the SNAPSHOT dependencies, which looks something like 1.0-SNAPSHOT
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven-project</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
Enter fullscreen mode
Exit fullscreen mode
When the software is ready for release, we generally create a RELEASE version which looks like 1.0.RELEASE (ex: Spring Boot Starter Test dependency)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.6.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
Enter fullscreen mode
Exit fullscreen mode
Dependency Scopes
Each Maven Dependency can be categorized 6 different scopes.
Here are the list of scopes available:
- compile: This is the default scope if none is specified. Compile time dependencies are available in classpath of the project.
-
provided: Similar to compile scope, but indicates that the JDK or the underlying container will provide the dependency at run-time. The dependency will be available at compile time, but not packaged into the artifact.
An example of the usage is the javax.servlet-api dependency -
runtime: The dependencies defined with this scope will be only available at runtime but not at compile time.
An example of the usage:
Imagine if you are using MySQL Driver inside your project, you can add the dependency with scope as runtime, to ensure that the JDBC API abstraction is used instead of MySQL Driver API during implementation.
If the source code includes any classes which is part of the MySQL JDBC API, then the code wont compile as the dependency is unavailable at compile time. - test: Dependencies are only available at the time of running the tests, typical examples include Junit and TestNG
- system: It’s similar to the provided scope, but only difference is we need to explicitly mention where can the dependency be found in the system, using the systemPath tag:
<systemPath>${basedir}/lib/some-dependency.jar</systemPath>
Repositories
In the previous section, we saw how Maven manages and automatically downloads the dependencies.
These dependencies, are stored inside a special directory called Repository. There are basically 2 kinds of repositories:
-
Local Repository
A Local Repository is a directory on the machine where the Maven is running.
The default location for the Local Repository is /.m2
In Windows, it looks like:
C:Users<user-name>.m2repository
-
Remote Repository
A Remote Repository is a web-site where we can download Maven Dependencies. This can be a repository provided by Maven (repo.maven.org) or a custom repository setup inside an organization using software like Artifactory or Nexus.
Now that we saw different types of repositories, let’s see how Maven Resolves the dependencies.
Now let’s see how Maven Resolves the dependencies.
- When you define a dependency defined inside the pom.xml file, Maven first checks whether the dependency is already present in the Local Repository or not.
- If it is not, then it tries to connect to the Remote Repository, (Ex: https://repo.maven.org) and tries to download the dependencies, and store them inside the Local Repository.
We can define the Remote Repository in our pom.xml like below:
<repositories>
<repository>
<id>my-internal-site</id>
<url>http://myserver/repo</url>
</repository>
</repositories>
Enter fullscreen mode
Exit fullscreen mode
Snapshot and Release Version Handling
In the previous section, we learned that Maven first checks the Local Repository before downloading a dependency from Remote Repository.
When dealing with SNAPSHOT dependencies Maven periodically downloads the dependency from Remote Repository even though the dependency exists in Local Repository.
This is because SNAPSHOT dependencies are under heavy development, and are subjected to change frequently.
You can change this behavior by adding a section inside the repositories tag.
<repositories>
<repository>
<id>my-internal-site</id>
<url>http://myserver/repo</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>XXX</updatePolicy>
</snapshots>
</repository>
</repositories>
Enter fullscreen mode
Exit fullscreen mode
The value XXX inside the updatePolicy can be:
- always: Maven always checks for a latest version
- daily: This is the default value, as the name suggests it downloads the version once every day on the first run.
- interval:XXX: Checks every XXX minutes
- never: Never checks for the updates.
RELEASE versions on the other hand are more stable, and follow the usual dependency resolution.
Maven Build Lifecycle
Till now we learned about the Core Concepts of Maven, now it’s time to dive deep and understand how Maven builds our Java Project.
Maven follows a Build Lifecycle to well… build our project.
This Lifecycle is divided into 3 parts:
- default
- clean
- site
Each Lifecycle is independent of each other and they can be executed together.
The default life cycle is divided into different phases like below:
- validate – verifies whether the pom.xml file is valid or not
- compile – compiles the source code inside the project
- test – runs unit-tests inside the project
- package – packages the source code into an artifact (ZIP, JAR, WAR or EAR)
- integration-test– executes tests marked as Integration Tests
- verify – checks whether the created package is valid or not.
- install – installs the created package into our Local Repository
- deploy – deploys the created package to the Remote Repository
The clean lifecycle is mainly responsible to clean the .class and meta-data generated by the above build phases.
The site lifecycle phase is responsible to generate Java Documentation.
All 3 lifecycles also contains some additional phases, which I am not going to cover in this tutorial, if you are interested you can refer to the Maven Documentation
Plugins and Goals
To be able to execute these Lifecycle Phases, Maven provides us with Plugins to perform each task.
Each plugin is associated with a particular Goal
Let’s have a look at different Plugins and the associated Goals:
Maven Compile Plugin
The Maven Compile Plugin is responsible to compile our Java files into the .class files. It’s equivalent of running javac
This plugin enables the compile phase of the default lifecycle.
You can add this plugin to your project by adding the below section to your pom.xml file under the dependencies
section.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
Enter fullscreen mode
Exit fullscreen mode
At the time of writing this tutorial, Maven Compiler Plugin is at version 3.8.1
After adding the plugin to the project, you can activate the compile phase, by typing the below command:
$ mvn compiler:compile
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-cli) @ maven-project ---
[INFO] Nothing to compile - all classes are up to date
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.729 s
[INFO] Finished at: 2020-12-04T20:16:23+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
In the above command the compiler is the name of the plugin and compile is the goal which triggers the lifecycle phase – compile
You can already see the message – “Nothing to compile – all classes are up to date”
As there are no Java Files in our project, there is nothing to compile, so let’s create a simple HelloWorld.java file
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
Enter fullscreen mode
Exit fullscreen mode
If you try to compile again, you can see the following output:
$ mvn compiler:compile
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-cli) @ maven-project ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projecttargetclasses
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.123 s
[INFO] Finished at: 2020-12-04T20:14:55+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
We can see the generated class files under the target folder which resides just under the project root folder.
If you have some Test files, those are also compiled using the Compiler Plugin.
To demonstrate that let’s create the below Test class under the src/test/java folder
public class HelloWorldTest {
public static void main(String[] args) {
HelloWorld helloWorld = new HelloWorld();
System.out.println(helloWorld.say("Hi"));
}
}
Enter fullscreen mode
Exit fullscreen mode
And here is how you can compile the test classes:
$ mvn compiler:testCompile
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-cli) @ maven-project ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projecttargettest-classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.319 s
[INFO] Finished at: 2020-12-04T20:23:59+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
As you can see that a change has been detected by the Maven Compiler Plugin and it has compiled our Test classes to the target/test-classes folder
As we are using IntelliJ, we can also use it to run the compilation, by clicking on the compile button under the LifeCycle dropdown as you see in the below image:
If you run the compile option you may see the below error:
[INFO] Compiling 1 source file to F:maven-projecttargetclasses
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] Source option 6 is no longer supported. Use 7 or later.
[ERROR] Target option 6 is no longer supported. Use 7 or later.
Enter fullscreen mode
Exit fullscreen mode
This is because, by default the Source and Target Compiler Version is set to 1.5 in IntelliJ Configuration. You can add the below configuration to the compiler plugin to override this settings:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>15</source>
<target>15</target>
</configuration>
</plugin>
</plugins>
</build>
Enter fullscreen mode
Exit fullscreen mode
You can pass many customized option to the Maven Compiler Plugin based on your needs, you can find the examples of the configuration in the Apache Maven Compiler Plugin website
Maven Surefire Plugin
Using Surefire Plugin, we can run the tests inside our project by using the following command:
$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-project ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ maven-project ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projecttargetclasses
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven-project ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory F:maven-projectsrctestresources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ maven-project ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projecttargettest-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ maven-project ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running HelloWorldTest
Hi
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.014 s - in HelloWorldTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.797 s
[INFO] Finished at: 2020-12-04T20:54:49+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
You can see that the HelloWorldTest.java file was executed successfully, and the you can also see how many Tests are executed successfully.
The Surefire Plugin generates Text and XML reports under the target/surefire-reports folder.
Similar to Compiler Plugin, you can also configure Surefire Plugin based on your needs.
Surefire Plugin by default includes all tests, if you have a bunch of tests, you can manually exclude some tests to be executed using below configuration:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<excludes>HelloWorldTest.java</excludes>
</configuration>
</plugin>
Enter fullscreen mode
Exit fullscreen mode
And if you run the mvn test command now, you can see that no tests are executed.
$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-project ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ maven-project ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven-project ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory F:maven-projectsrctestresources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ maven-project ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ maven-project ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.043 s
[INFO] Finished at: 2020-12-04T21:00:09+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
Maven Install Plugin
This is used to package our source code into an artifact of our choice like a JAR and install it to our Local Repository which is /.m2/repository folder.
You can configure Maven Install Plugin by adding the below code:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
Enter fullscreen mode
Exit fullscreen mode
You can run the install phase of the lifecycle by typing the below command:
$ mvn install
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-project ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ maven-project ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven-project ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory F:maven-projectsrctestresources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ maven-project ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ maven-project ---
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ maven-project ---
[INFO] Building jar: F:maven-projecttargetmaven-project-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ maven-project ---
[INFO] Installing F:maven-projecttargetmaven-project-1.0-SNAPSHOT.jar to C:Userssai.m2repositoryorgexamplemaven-project1.0-SNAPSHOTmaven-project-1.0-SNAPSHOT.jar
[INFO] Installing F:maven-projectpom.xml to C:Userssai.m2repositoryorgexamplemaven-project1.0-SNAPSHOTmaven-project-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.236 s
[INFO] Finished at: 2020-12-04T21:10:49+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
By checking the above output, you can see that the install phase includes also the previous phases in the lifecycle, so as part of this phase maven:
- validates our pom.xml (validate)
- compiles our source code (compile)
- executes our tests (test)
- packages our source code into JAR (package)
- installs the JAR into our local repository (install)
You can see the JAR file in the below screenshot under the target folder.
Maven Deploy Plugin
The Maven Deploy Plugin will deploy the artifact into a remote repository. The deploy goal of the plugin is associated with the deploy phase of the build lifecycle.
The plugin can be configured like below:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
Enter fullscreen mode
Exit fullscreen mode
Before running the deploy phase of the lifecycle, we have to make sure that the remote repository details are configured inside our project.
We can configure this details inside the distributionManagement section:
<distributionManagement>
<repository>
<id>test-distribution</id>
<name>distro name</name>
<url>http://distrourl.com</url>
</repository>
</distributionManagement>
Enter fullscreen mode
Exit fullscreen mode
To be able to connect to the remote repository, maven needs access to the credentials, which can be configured inside a special file called as settings.xml file.
This file is usually configured inside the /.m2/ folder, which looks like something below:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>test-distribution</id>
<username>my_username</username>
<password>my_password</password>
</server>
</servers>
</settings>
Enter fullscreen mode
Exit fullscreen mode
Maven also provides us a way to encrypt the credentials inside the settings.xml file, you can read about this here
You can run the deploy goal using the following command:
$ mvn deploy
Once you run this command, you will notice that it will run all the lifecycle phases up until deploy.
Although if you run this command in the example project, it will fail because the Remote Repository Details are invalid.
Maven Clean Plugin
Another important plugin in Maven is the Maven Clean Plugin, you saw that when running the above lifecycle phases, the generated files are stored under a folder called target.
Usually when building our source code we need to start of as a clean slate so that there are no inconsistencies in the generated class files or JAR.
For this reason we have the clean phase where we will delete all the contents inside the target folder. You can execute this phase by typing the below commands:
$ mvn clean
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ maven-project ---
[INFO] Deleting F:maven-projecttarget
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.204 s
[INFO] Finished at: 2020-12-04T21:19:12+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
You can see that the target folder under our project is deleted successfully.
Other Maven Plugins
There are many other plugins in Maven world, to execute other phases in the Maven Build Lifecycle, you can check them out here
Maven Multi Module Projects
Till now we saw the Core Concepts of Maven, now it’s time to dive deep into some advanced concepts.
In the real world where we are building some non-trivial projects, the source code is going to be modularized (hopefully) and is divided into different sub projects.
To manage this effectively, Maven provides us Multi Module Projects where you can have nest different projects inside each other. We are basically creating a parent-child relationship between different Maven projects.
Basically, we have a Parent Project (Parent POM) which contains different sub-projects (sub-modules), each of which is again a normal Maven Project.
The Parent POM usually encapsulates other child’s and that’s why its packaged as a POM instead of usual packaing format’s like JAR.
If you recall the section Project Object Model , we discussed about Super POM which is basically a kind of Parent POM which encapsulates default settings configured by Maven for us.
Creating Multi Module Project using IntelliJ
We will see how to generate Multi Module Projects using our IntelliJ IDE.
To create the project, first right click on the project root folder and select New -> Module and click on Next.
Give a name to the child project and click on Finish.
You can see the new project structure in the above picture after we created the child project.
Now if you open the pom.xml under the root folder, you can observe the following tags which are added by creating the maven module.
<modules>
<module>child-project-1</module>
</modules>
Enter fullscreen mode
Exit fullscreen mode
We can create multiple projects in the same way and you can see all the modules will be listed one-by-one under the modules tag.
<modules>
<module>child-project-1</module>
<module>child-project-2</module>
<module>child-project-3</module>
</modules>
Enter fullscreen mode
Exit fullscreen mode
As we create these 3 modules, having the src folder under the root project folder doesn’t make much sense, so let’s copy and paste this folder onto other child-projects, so that we have some code configured inside these modules.
This is how our project structure now looks like:
And if you check the pom.xml of child-project-1,child-project-2 and child-project-3. You can see that the root project is configured as a parent project.
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>maven-project</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>child-project-1</artifactId>
</project>
Enter fullscreen mode
Exit fullscreen mode
Now you can build all the projects at once by running the mvn install under the Parent Project, and Maven scans through all the POMS and builds all of them one-by-one
$ mvn install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] maven-project [pom]
[INFO] child-project-1 [jar]
[INFO] child-project-2 [jar]
[INFO] child-project-3 [jar]
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ maven-project ---
[INFO] Installing F:maven-projectpom.xml to C:Userssubra.m2repositoryorgexamplemaven-project1.0-SNAPSHOTmaven-project-1.0-SNAPSHOT.pom
[INFO]
[INFO] --------------------< org.example:child-project-1 >---------------------
[INFO] Building child-project-1 1.0-SNAPSHOT [2/4]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ child-project-1 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ child-project-1 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-1targetclasses
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ child-project-1 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory F:maven-projectchild-project-1srctestresources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ child-project-1 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-1targettest-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ child-project-1 ---
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ child-project-1 ---
[INFO] Building jar: F:maven-projectchild-project-1targetchild-project-1-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ child-project-1 ---
[INFO] Installing F:maven-projectchild-project-1targetchild-project-1-1.0-SNAPSHOT.jar to C:Userssubra.m2repositoryorgexamplechild-project-11.0-SNAPSHOTchild-project-1-1.0-SNAPSHOT.jar
[INFO] Installing F:maven-projectchild-project-1pom.xml to C:Userssubra.m2repositoryorgexamplechild-project-11.0-SNAPSHOTchild-project-1-1.0-SNAPSHOT.pom
[INFO]
[INFO] --------------------< org.example:child-project-2 >---------------------
[INFO] Building child-project-2 1.0-SNAPSHOT [3/4]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ child-project-2 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ child-project-2 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-2targetclasses
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ child-project-2 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory F:maven-projectchild-project-2srctestresources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ child-project-2 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-2targettest-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ child-project-2 ---
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ child-project-2 ---
[INFO] Building jar: F:maven-projectchild-project-2targetchild-project-2-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ child-project-2 ---
[INFO] Installing F:maven-projectchild-project-2targetchild-project-2-1.0-SNAPSHOT.jar to C:Userssubra.m2repositoryorgexamplechild-project-21.0-SNAPSHOTchild-project-2-1.0-SNAPSHOT.jar
[INFO] Installing F:maven-projectchild-project-2pom.xml to C:Userssubra.m2repositoryorgexamplechild-project-21.0-SNAPSHOTchild-project-2-1.0-SNAPSHOT.pom
[INFO]
[INFO] --------------------< org.example:child-project-3 >---------------------
[INFO] Building child-project-3 1.0-SNAPSHOT [4/4]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ child-project-3 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ child-project-3 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-3targetclasses
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ child-project-3 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory F:maven-projectchild-project-3srctestresources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ child-project-3 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-3targettest-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ child-project-3 ---
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ child-project-3 ---
[INFO] Building jar: F:maven-projectchild-project-3targetchild-project-3-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ child-project-3 ---
[INFO] Installing F:maven-projectchild-project-3targetchild-project-3-1.0-SNAPSHOT.jar to C:Userssubra.m2repositoryorgexamplechild-project-31.0-SNAPSHOTchild-project-3-1.0-SNAPSHOT.jar
[INFO] Installing F:maven-projectchild-project-3pom.xml to C:Userssubra.m2repositoryorgexamplechild-project-31.0-SNAPSHOTchild-project-3-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for maven-project 1.0-SNAPSHOT:
[INFO]
[INFO] maven-project ...................................... SUCCESS [ 0.252 s]
[INFO] child-project-1 .................................... SUCCESS [ 1.534 s]
[INFO] child-project-2 .................................... SUCCESS [ 0.430 s]
[INFO] child-project-3 .................................... SUCCESS [ 0.351 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.699 s
[INFO] Finished at: 2020-12-04T21:58:10+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
You can see that Maven built all the projects and provided us a nice report of the Build Status of each project.
Maven uses something called as a Reactor which is responsible to scan the whole project and identify the parent and child maven projects. If there are any dependencies, then Reactor makes sure to execute the projects in the required order.
For example, if child-project-2 is dependent on child-project-3 , then Maven Reactor makes sure to first build child-project-3 and then child-project-2.
Managing Dependencies inside Maven Multi Module Project
When you are working with Multiple Maven Modules, you may be working with different dependencies in different modules, and chances are that you may be using similar dependencies in multiple modules.
Maven provides us a way to effectively manage the dependencies in your project by allowing us to define the dependencies in centralized location (parent project) and use those dependencies across the different child projects.
This minimizes the dependency version mismatch across multiple projects, as we have a single place we can manage all the dependencies and its versions.
Let’s see how to do that in our example project.
I am going to move all the dependencies which are defined inside the parent project into the dependencyManagement section, like below:
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven-project</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>child-project-1</module>
<module>child-project-2</module>
<module>child-project-3</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.6.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<excludes>HelloWorldTest.java</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
</plugins>
</build>
</project>
Enter fullscreen mode
Exit fullscreen mode
The change in the pom.xml is minor, we just moved the dependencies under the dependencyManagement tag.
Now if you analyze the dependencies under the child-project-1 you can see that it’s empty.
mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< org.example:child-project-1 >---------------------
[INFO] Building child-project-1 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ child-project-1 ---
[INFO] org.example:child-project-1:jar:1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.871 s
[INFO] Finished at: 2020-12-05T20:23:59+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
This is because we moved all the dependencies inside the Parent POM into dependencyManagement section, now to access any dependencies the Child Projects should define them manually inside their pom.xml.
In this way, the child projects can have only the dependencies they need inside the pom.xml.
Now, let’s go ahead and add the junit-jupiter dependencies inside the child-project-1 and see what happens.
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>maven-project</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>child-project-1</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
</dependency>
</dependencies>
</project>
Enter fullscreen mode
Exit fullscreen mode
$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< org.example:child-project-1 >---------------------
[INFO] Building child-project-1 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ child-project-1 ---
[INFO] org.example:child-project-1:jar:1.0-SNAPSHOT
[INFO] - org.junit.jupiter:junit-jupiter-engine:jar:5.5.2:compile
[INFO] +- org.apiguardian:apiguardian-api:jar:1.1.0:compile
[INFO] +- org.junit.platform:junit-platform-engine:jar:1.5.2:compile
[INFO] | +- org.opentest4j:opentest4j:jar:1.2.0:compile
[INFO] | - org.junit.platform:junit-platform-commons:jar:1.5.2:compile
[INFO] - org.junit.jupiter:junit-jupiter-api:jar:5.5.2:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.930 s
[INFO] Finished at: 2020-12-05T20:27:26+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
You can now see that the junit-jupiter dependency is available inside the child-project-1.
Managing Plugins inside Maven Multi Module Project
We can also manage Plugins inside our Maven Multi Module project, similar to the dependencies.
To Manage Plugins, we have the pluginManagement section inside the pom.xml and each child project can chose the plugin it needs.
This is how our parent pom.xml looks like after introducing the pluginManagement.
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven-project</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>child-project-1</module>
<module>child-project-2</module>
<module>child-project-3</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.6.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<excludes>HelloWorldTest.java</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Enter fullscreen mode
Exit fullscreen mode
Using Profiles
Profiles can be used in Maven to created customized build configurations. This means customizing the behavior of our builds based on specific conditions.
For example: Sometimes we need to test whether the source code packaging is working correctly or not, in that case we can skip the test execution by activating the skip.tests property like below:
<profile>
<id>skip-tests</id>
<properties>
<maven.test.skip>true</maven.test.skip>
</properties>
</profile>
Enter fullscreen mode
Exit fullscreen mode
Now we can try to run the build by specifying which profile to activate using the -P flag.
$ mvn -Pskip-tests clean install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] maven-project [pom]
[INFO] child-project-1 [jar]
[INFO] child-project-2 [jar]
[INFO] child-project-3 [jar]
[INFO]
[INFO] ---------------------< org.example:maven-project >----------------------
[INFO] Building maven-project 1.0-SNAPSHOT [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ maven-project ---
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ maven-project ---
[INFO] Installing F:maven-projectpom.xml to C:Userssubra.m2repositoryorgexamplemaven-project1.0-SNAPSHOTmaven-project-1.0-SNAPSHOT.pom
[INFO]
[INFO] --------------------< org.example:child-project-1 >---------------------
[INFO] Building child-project-1 1.0-SNAPSHOT [2/4]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ child-project-1 ---
[INFO] Deleting F:maven-projectchild-project-1target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ child-project-1 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ child-project-1 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-1targetclasses
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ child-project-1 ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ child-project-1 ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ child-project-1 ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ child-project-1 ---
[INFO] Building jar: F:maven-projectchild-project-1targetchild-project-1-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ child-project-1 ---
[INFO] Installing F:maven-projectchild-project-1targetchild-project-1-1.0-SNAPSHOT.jar to C:Userssubra.m2repositoryorgexamplechild-project-11.0-SNAPSHOTchild-project-1-1.0-SNAPSHOT.jar
[INFO] Installing F:maven-projectchild-project-1pom.xml to C:Userssubra.m2repositoryorgexamplechild-project-11.0-SNAPSHOTchild-project-1-1.0-SNAPSHOT.pom
[INFO]
[INFO] --------------------< org.example:child-project-2 >---------------------
[INFO] Building child-project-2 1.0-SNAPSHOT [3/4]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ child-project-2 ---
[INFO] Deleting F:maven-projectchild-project-2target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ child-project-2 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ child-project-2 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-2targetclasses
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ child-project-2 ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ child-project-2 ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ child-project-2 ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ child-project-2 ---
[INFO] Building jar: F:maven-projectchild-project-2targetchild-project-2-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ child-project-2 ---
[INFO] Installing F:maven-projectchild-project-2targetchild-project-2-1.0-SNAPSHOT.jar to C:Userssubra.m2repositoryorgexamplechild-project-21.0-SNAPSHOTchild-project-2-1.0-SNAPSHOT.jar
[INFO] Installing F:maven-projectchild-project-2pom.xml to C:Userssubra.m2repositoryorgexamplechild-project-21.0-SNAPSHOTchild-project-2-1.0-SNAPSHOT.pom
[INFO]
[INFO] --------------------< org.example:child-project-3 >---------------------
[INFO] Building child-project-3 1.0-SNAPSHOT [4/4]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ child-project-3 ---
[INFO] Deleting F:maven-projectchild-project-3target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ child-project-3 ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ child-project-3 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:maven-projectchild-project-3targetclasses
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ child-project-3 ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ child-project-3 ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ child-project-3 ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ child-project-3 ---
[INFO] Building jar: F:maven-projectchild-project-3targetchild-project-3-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ child-project-3 ---
[INFO] Installing F:maven-projectchild-project-3targetchild-project-3-1.0-SNAPSHOT.jar to C:Userssubra.m2repositoryorgexamplechild-project-31.0-SNAPSHOTchild-project-3-1.0-SNAPSHOT.jar
[INFO] Installing F:maven-projectchild-project-3pom.xml to C:Userssubra.m2repositoryorgexamplechild-project-31.0-SNAPSHOTchild-project-3-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for maven-project 1.0-SNAPSHOT:
[INFO]
[INFO] maven-project ...................................... SUCCESS [ 0.327 s]
[INFO] child-project-1 .................................... SUCCESS [ 1.208 s]
[INFO] child-project-2 .................................... SUCCESS [ 0.205 s]
[INFO] child-project-3 .................................... SUCCESS [ 0.167 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.055 s
[INFO] Finished at: 2020-12-05T23:00:13+01:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode
Exit fullscreen mode
If you observe the logs carefully, you can see that the test-execution is skipped:
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ child-project-3 ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ child-project-3 ---
[INFO] Building jar: F:maven-projectchild-project-3targetchild-project-3-1.0-SNAPSHOT.jar
Enter fullscreen mode
Exit fullscreen mode
This is just a simple usage of the profiles, you can configure multiple profiles in our project to customize the build behavior.
Conclusion
So we came to the end of this tutorial, I hope this tutorial improved your understanding of Maven.
If you like this article please make sure to share it with your friends and colleagues who wants to learn about Apache Maven. You can follow me through my blog, Youtube Channel and Twitter
Maven — инструмент для автоматизации сборки проектов. С ним работают в основном Java-разработчики, хотя есть плагины для интеграции с C/C++, Ruby, Scala, PHP и другими языками. В статье будут рассмотрены особенности и область применения Maven, описан процесс установки и начала работы, а также разобрана структура файла описания проекта.
Назначение и особенности
Собрать на Java проект уровня «Hello, world!» можно и с помощью командной строки. Но чем сложнее разрабатываемое ПО и чем больше оно использует сторонних библиотек и ресурсов, тем сложнее будет команда для сборки. Maven разработан для облегчения этой работы.
Одна из главных особенностей фреймворка — декларативное описание проекта. Это значит, что разработчику не нужно уделять внимание каждому аспекту сборки — все необходимые параметры настроены по умолчанию. Изменения нужно вносить лишь в том объёме, в котором программист хочет отклониться от стандартных настроек.
Ещё одно достоинство проекта — гибкое управление зависимостями. Maven умеет подгружать в свой локальный репозиторий сторонние библиотеки, выбирать необходимую версию пакета, обрабатывать транзитивные зависимости.
Разработчики также подчёркивают независимость фреймворка от ОС. При работе из командной строки параметры зависят от платформы, но Maven позволяет не обращать внимания на этот аспект.
При необходимости систему сборки можно настроить под собственные нужды, используя готовые плагины и архетипы. А если ничего подходящего не нашлось — можно написать свои.
В этой статье мы будем работать с Maven с помощью командной строки, однако этот фреймворк также интегрирован в Eclipse, IntelliJ IDEA, NetBeans и другие IDE.
Установка, настройка и создание стандартного проекта
Скачать Maven можно с официальной страницы проекта. Там же вас познакомят с минимальными требованиями — на машине должен быть установлен JDK, а также потребуется свободное место на диске, около 500 мегабайт. Это место нужно не для самой установки, оно будет использовано для создания локального репозитория.
На странице скачивания вы найдёте несколько разных архивов, для первого раза лучше использовать готовые бинарники. Исходники потребуются, если захочется всё сломать покопаться в Maven.
Архив можно распаковать в любое удобное место. После этого нужно добавить путь к папке bin
из распакованного архива в переменную среды PATH
. В Windows нужно зайти в настройки параметров системы (вызывается комбинацией клавиш Win+Pause или щелчком правой кнопкой мыши по ярлыку «Мой / Этот компьютер» -> «Свойства»), и выбрать пункт «Дополнительные параметры системы». В правом нижнем углу нажмите кнопку «Переменные среды». Выберите переменную PATH
, нажмите «Изменить», в открывшемся окне — «Создать» и добавьте путь. Обратите внимание, путь должен вести именно к папке bin
.
В ОС на основе Unix переменную среды можно добавить консольной командной:
export PATH=/opt/apache-maven-3.6.0/bin:$PATH
Проверить, всё ли сделано правильно, можно с помощью консольной команды mvn -v
. Вы должны увидеть что-то подобное:
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-24T21:41:47+03:00)
Maven home: C:apache-maven-3.6.0bin..
Java version: 10.0.1, vendor: Oracle Corporation, runtime: C:Program FilesJavajdk-10.0.1
Default locale: ru_RU, platform encoding: Cp1251
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
Создаём папку для нового проекта и переходим в неё. Далее создаём новый проект. Для этого в консоли выполните команду:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
Выглядит довольно сложно, поэтому разберём её по порядку.
archetype:generate
это так называемая цель. Она указывает Maven, что нужно создать проект из архетипа. Дальше с помощью -D мы указываем определённые параметры генерации.
groupId=com.mycompany.app
указывает на разработчика ПО, там вы можете указать доменное имя своей компании.artifactId=my-app
— название проекта. Maven оперирует так называемыми артефактами. Это приложения, плагины, архетипы и другие проекты. И ваша разработка также будет считаться артефактом.archetypeArtifactId=maven-archetype-quickstart
указывает, какой архетип использовать в качестве шаблона для создания приложения. Как видите, это тоже артефакт. Указанный архетип создаст простой проект, сгенерирует структуру каталогов и даже заботливо положит в папку исходников программу «Hello, world!», чтобы вам не пришлось самому писать её в двухсотый раз.archetypeVersion=1.4
указывает на версию артефакта «архетип».interactiveMode=false
отключает создание проекта в интерактивном режиме. Вы можете запустить цельarchetype:generate
без параметров, и Maven предложит установить параметры в процессе генерации. В данном случае нам это не нужно. Кстати, отключить интерактивный режим можно параметром-B
. Полный список параметров дляmvn
можно получить по командеmvn -h
.
Выполнив команду, Maven сгенерирует следующую структуру проекта:
my-app
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- com
| `-- mycompany
| `-- app
| `-- App.java
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
Исходники нашего проекта будут лежать в папке src/main/java
(и сейчас там уже есть автоматически сгенерированный файл), но нам сейчас интереснее файл pom.xml
в корневом каталоге. Это и есть тот самый файл описания проекта, на основе которого осуществляются все операции Maven. Он написан на языке POM, входящим в семейство XML:
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
<name>my-app</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins><...></plugins>
</pluginManagement>
</build>
</project>
Разберёмся, что всё это значит.
Что значат теги в pom.xml
Тег project
является базовым и содержит всю информацию о проекте. В заголовке указана информация, необходимая Maven для понимания файла pom.xml
. Тег modelVersion
указывает на текущую версию POM. Эти два тега обычно генерируются автоматически, менять их не нужно.
Затем идёт информация, формирующая уникальный идентификатор проекта, теги groupId
и artifactId
. Её мы задавали выше при генерации из архетипа. Тег version
тоже входит в эту группу. Он обычно генерируется и обновляется автоматически. После номера версии идёт суффикс -SNAPSHOT
. Это означает, что проект находится в стадии разработки. В процессе выпуска ПО фреймворк уберёт этот суффикс, а если разработка продолжится — автоматически увеличит номер версии. Вместе эти три тега позволяют однозначно идентифицировать артефакт.
name
содержит отображаемое имя артефакта, а url
— ссылку на сайт. Поскольку сайт не задан при генерации, pom.xml
содержит напоминание об этом в виде комментария. Кроме того, можно добавить краткое описание в description
. Эти три тега зачастую используются при формировании документации.
Дальше мы видим блок properties
. Здесь указаны особые настройки, такие как кодировка файла и используемая версия компилятора Java. Без этого блока можно обойтись, положившись на настройки по умолчанию.
Следом идёт очень важный блок dependencies
. В нём описываются все используемые в проекте зависимости. Каждую необходимо выделить тегом dependency
и указать уникальные идентификационные данные: groupId
, artifactId
и version
. Maven сам подгрузит транзитивные зависимости. Кроме того, с помощью тега scope
можно указать этапы, на которых будет использован артефакт. Сейчас в pom.xml
всего одна зависимость — артефакт JUnit, библиотека для модульного тестирования на Java, которая будет использоваться только на стадии тестирования.
Кстати, это хороший повод поговорить о жизненном цикле проекта. Maven выполняет сборку последовательными фазами. Приводим их названия на английском, так как они используются в качестве команд.
- Проверка —
validate
. Фреймворк проверяет, корректен ли проект и предоставлена ли вся необходимая для сборки информация. - Компиляция —
compile
. Maven компилирует исходники проекта. - Тест —
test
. Проверка скомпилированных файлов. В нашем случае будет использована библиотека JUnit. - Сборка проекта —
package
. По умолчанию осуществляется в формате JAR. Этот параметр можно изменить, добавив вproject
тегpackaging
. - Интеграционное тестирование —
integration-test
. Maven обрабатывает и при необходимости распаковывает пакет в среду, где будут выполняться интеграционные тесты. - Верификация —
verify
. Артефакт проверяется на соответствие критериям качества. - Инсталляция —
install
. Артефакт попадает в локальный репозиторий. Теперь его можно использовать в качестве зависимости. - Размещение проекта в удалённом репозитории —
deploy
, — финальная стадия работы.
Эти фазы упорядочены и выполняются поочерёдно. Если необходимо собрать проект, система последовательно проведёт оценку, компиляцию и тестирование, и только после этого сборку. Помимо этого есть две фазы, выполняющиеся отдельно, только прямой командой. Это очистка — clean
, удаляющая предыдущие сборки, и создание документации для сайта — site
.
Закончим рассмотрение pom.xml
секцией build
. Она не является обязательной, в данном pom.xml
в неё включены плагины из архетипа, однако можно обойтись и без них. Плагинов для Maven тысячи, освоившись с применением фреймворка вы сможете сами подобрать себе необходимые.
В проектах чуть серьёзнее, чем вычисление факториала, приходится использовать внешние ресурсы. Maven способен автоматически обрабатывать файлы ресурсов и размещать их в сборке проекта. Для этого их нужно разместить в папке src/main/resources
. Файлы будут упакованы с сохранением внутренней структуры каталогов. Если же по какой-то причине нужно переопределить каталог, используйте вложенные теги resources
, resource
, directory
в секции build
:
<build>
<resources>
<resource>
<directory>src/main/another_resources_directory</directory>
</resource>
</resources>
</build>
Итак, с файлом описания мы разобрались. Попробуем собрать проект. Для этого перейдём в корневую папку и выполним команду нужной фазы, mvn package
. Получим отчёт о сборке:
[INFO] ----------------------< com.mycompany.app:my-app >----------------------
[INFO] Building my-app 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ my-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:javaProjectsnew_projectmy-appsrcmainresources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ my-app ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to C:javaProjectsnew_projectmy-apptargetclasses
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ my-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:javaProjectsnew_projectmy-appsrctestresources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ my-app ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to C:javaProjectsnew_projectmy-apptargettest-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ my-app ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.mycompany.app.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.217 s - in com.mycompany.app.AppTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ my-app ---
[INFO] Building jar: C:javaProjectsnew_projectmy-apptargetmy-app-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 20.670 s
[INFO] Finished at: 2019-02-17T02:25:07+03:00
[INFO] ------------------------------------------------------------------------
Теперь в корневом каталоге проекта вы обнаружите папку target, а в ней готовый файл my-app-1.0-SNAPSHOT.jar
.
Запустите команду java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
— вы должны увидеть «Hello World!».
Поздравляем! Вы собрали проект с помощью Maven. Для более детального изучения инструмента советуем обратить внимание на эти книги и материалы.