Maven: Goals и Phases

На первый взгляд сборка Maven выглядит магией – мы выполняем простую команду и запускается целый жизненный цикл, состоящий из фаз, к каждой из которой привязан свой плагин и goal (иногда не один).
Давайте разберемся, что такое фазы, голы, и как они соотносятся между собой.

Как соотносятся phases и goals

Сборку с помощью Maven можно сравнить с обедом из нескольких блюд: в нем есть закуска, первое блюдо, второе блюдо, напиток и десерт – всё это фазы (phases), а все вместе – жизненный цикл. Второе блюдо может быть котлетой, а может рыбой. Конкретика задается с помощью goal (конкретная команда конкретного плагина). Например, на фазе compile по умолчанию выполняется compiler:compile – это goal с названием compile плагина compiler. Эта команда компилирует исходники. А десерт может быть шарлоткой, это тоже конкретный goal, например deploy:deploy фазы deploy.

К каждой фазе можно привязать несколько голов, а можно ни одного – тогда фаза просто пропускается при сборке.

Сложность состоит в том, что набор умолчаний в Maven велик –  согласно документации, в жизненном цикле много зарезервированных фаз, в которых ничего не выполняется (например, фаза validate). Эти фазы подразумевают какой-то смысл, но зарезервированы на тот случай, если программист захочет к ним привязать какое-то свое действие.
Конкретно на фазе validate должна проверяться корректность проекта, но как именно, решает программист, если ему это надо. (А если не надо, фаза validate пропускается). Ниже мы попробуем привязать к этой фазе простейшее действие.

Есть также фазы, которые, наоборот, выполняются по умолчанию – например, фаза compile. К ней по умолчанию привязан свой плагин и goal, как уже упоминалось выше.

Узнать, что именно происходит при сборке, можно, например, запустив сборку – все goals выведутся в консоль.

Можно также вывести все goals данной фазы так:

mvn help:describe -Dcmd=<фаза>

Попробуем запросить все goal фазы compile:

mvn help:describe -Dcmd=compile

Ответ такой:

compile' is a phase corresponding to this plugin:
org.apache.maven.plugins:maven-compiler-plugin:3.6:compile

То есть на фазе compile по умолчанию выполняется maven-compiler-plugin. Через двоеточие указана goal, она называется compile.

Default build lifecycle

Запустим сборку, указав фазу install:

mvn install

Обратите внимание, что pom.xml практически пустой – в нем не перечислены никакие фазы и goals, они все привязаны по умолчанию к сборке jar-файла:

<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>ru.sysout</groupId>
	<artifactId>example-maven-project</artifactId>
	<version>1.0</version>
</project>

При этом в pom.xml даже не указано, что надо собрать jar-файл – этот формат также подразумевается по умолчанию.

Вывод в консоль показывает, какие плагины и голы выполняются при сборке:

[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ example-maven-project ---
[WARNING] Using platform encoding (Cp1251 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ example-maven-project ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ example-maven-project ---
[WARNING] Using platform encoding (Cp1251 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\Code\sysout\example-maven-project\src\test\resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ example-maven-project ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ example-maven-project ---
[INFO] 
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ example-maven-project ---
[INFO] Building jar: C:\Code\sysout\example-maven-project\target\example-maven-project-1.0.jar
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ example-maven-project ---
[INFO] Installing C:\...\.m2\repository\ru\sysout\example-maven-project\1.0\example-maven-project-1.0.jar
[INFO] Installing C:\...\.m2\repository\ru\sysout\example-maven-project\1.0\example-maven-project-1.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

Как видно, по очереди выполняется:

  • копирование папки ресурсов main\resources
  • компиляция исходников из папки main\java
  • копирование тестовых ресурсов test\resources
  • компиляция исходников из папки test\java
  • далее происходит выполнение тестов
  • упаковка в jar и
  • копирование jar файла в .m2 папку на локальном компьютере.

Есть в жизненном цикле еще фаза deploy – развертывание на сервер, эта последняя фаза тут не выполнялась.

Жизненный цикл (lifecycle) сборки – это последовательность фаз (phases).

Выполнение предыдущих фаз

Обратите внимание, что мы указали одну фазу install, но выполняется целый жизненный цикл со всеми предыдущими фазами, фаза install в этом цикле последняя.

Чтобы только скопировать ресурсы и скомпилировать проект, выполняем:

mvn compile

Чтобы, помимо этого, выполнить тесты и  упаковать  проект в jar, выполняем:

mvn package

(при этом выполнится компиляция и другие предваряющие package фазы).

Чтобы развернуть проект на сервере, выполним:

mvn deploy

Все предыдущие фазы, включая install, также при этом выполнятся. В pom.xml при этом должна быть указана конфигурация с данными сервера, куда деплоить проект.

В документации указана очередность фаз, она задана заранее. Там же указаны какие именно плагины и goals по умолчанию выполняются в каких фазах. Мы можем перезаписать или добавить свое действие к определенный фазе.

Попробуем добавить свое действие к двум фазам.

Как привязать к фазе свое действие

Во-первых, любое действие, которые вы добавляете к сборке, должно быть запрограммировано в виде Maven-плагина с goal (у плагина может быть и несколько goal, которые можно привязать к разным фазам). Этот плагин вы либо пишете сами, либо берете готовый. Готовых плагинов много.

Плагин – это java-приложение, упакованное в jar, как его написать, говорится здесь.

Во-вторых, чтобы добавить goal к фазе, вы прописываете в pom.xml этот плагин и goal, чуть ниже показано, как. Также можно указать фазу в теге <phase> (можно, а не нужно, потому что для плагина уже может существовать умолчательная фаза, заданная при программировании плагина; но ее может и не быть). Добавленный goal будет выполнен после всех уже привязанных по умолчанию к фазе goal-ов.

Для примера  возьмем плагин, разбираемый в документации по ссылке выше.  Он выводит в консоль “Hello, world”.  Скомпилируйте и установите его в локальную папку .m2:

mvn install

Теперь можно добавить выполнение goal hello-maven-plugin:sayhi в фазы своего проекта, например в фазы compile и validate. Для этого в корень нашего почти пустого pom.xml дабавьте:

	<build>
		<plugins>
			<plugin>
				<groupId>sample.plugin</groupId>
				<artifactId>hello-maven-plugin</artifactId>
				<version>1.0</version>
				<executions>
					<execution>
						<id>execution1</id>
						<phase>validate</phase>
						<goals>
							<goal>sayhi</goal>
						</goals>
					</execution>

					<execution>
						<id>execution2</id>
						<phase>compile</phase>
						<goals>
							<goal>sayhi</goal>
						</goals>
					</execution>

				</executions>
			</plugin>
		</plugins>
	</build>

При этом  сначала выполнится компиляция (так как этот goal уже привязан по умолчанию), потом напечатается “Hello, world”.

Что касается фазы validate, то поскольку по умолчанию к этой фазе ничего не привязано, выполнится только наш goal sayhi (причем в самом начале, так как validate – первая фаза согласно документации).

В консоли мы увидим:

[INFO] Building example-maven-project 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- hello-maven-plugin:1.0:sayhi (execution1) @ example-maven-project ---
[INFO] Hello, world.
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ example-maven-project ---
[WARNING] Using platform encoding (Cp1251 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ example-maven-project ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- hello-maven-plugin:1.0:sayhi (execution2) @ example-maven-project ---
[INFO] Hello, world.
........................................................

Кстати, наш goal можно выполнить и отдельно из командной строки:

mvn sample.plugin:hello-maven-plugin:1.0:sayhi

Какие еще есть жизненные циклы Maven

Хотя в работе чаще используется default lifecycle, о котором мы говорили выше, он не единственный.

Всего жизненных циклов у Maven определено три:

  • default – этот цикл отвечает за развертывание проекта (и все предыдущие, предваряющие развертывание действия).
  • clean – отвечает за удаление файлов, оставшихся с предыдущей сборки. Удаляет папку target.
  • site – отвечает за создание документации.

mvn clean install

Довольно часто указывают две фазы подряд:

mvn clean install

Да, это фазы, но они принадлежат разным циклам. Тут запускается два цикла по очереди – сначала цикл clean, а затем цикл default.

Фаза clean не входит в default цикл. Фаза clean – это часть одноименного цикла clean.

Мы не можем указать, например:

mvn validate install

Это фазы из одного и того же цикла default, они не могут быть выдернуты из цикла и выполнены вне его (помните правило выполнения всех предыдущих фаз).

Так что хотя многие и считают, что в примере mvn clean install указаны некоторые две выбранные фазы одного цикла, это ложная догадка,  на самом деле тут выполняются два цикла, а не две фазы.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *